VOL-1235: Adtran ONU: Add initial support of running without xPON

Change-Id: I2866a6989976a13729f91bc18a380dd7f26c1b65
diff --git a/voltha/adapters/adtran_onu/adtran_onu.py b/voltha/adapters/adtran_onu/adtran_onu.py
index 06bc4af..450273f 100755
--- a/voltha/adapters/adtran_onu/adtran_onu.py
+++ b/voltha/adapters/adtran_onu/adtran_onu.py
@@ -40,7 +40,7 @@
                                                device_handler_class=AdtranOnuHandler,
                                                name='adtran_onu',
                                                vendor='Adtran Inc.',
-                                               version='0.14',
+                                               version='0.15',
                                                device_type='adtran_onu',
                                                vendor_id='ADTN',
                                                accepts_add_remove_flow_updates=False),  # TODO: Support flow-mods
@@ -151,7 +151,6 @@
         API to create various interfaces (only some PON interfaces as of now)
         in the devices
         """
-
         self.log.debug('create-interface', data=data)
 
         handler = self.devices_handlers.get(device.id)
diff --git a/voltha/adapters/adtran_onu/adtran_onu_handler.py b/voltha/adapters/adtran_onu/adtran_onu_handler.py
index eab5b56..693d9bd 100644
--- a/voltha/adapters/adtran_onu/adtran_onu_handler.py
+++ b/voltha/adapters/adtran_onu/adtran_onu_handler.py
@@ -23,20 +23,23 @@
 from voltha.extensions.kpi.onu.onu_pm_metrics import OnuPmMetrics
 from voltha.extensions.kpi.onu.onu_omci_pm import OnuOmciPmMetrics
 
-from uuid import uuid4
 from twisted.internet import reactor
 from twisted.internet.defer import DeferredQueue, inlineCallbacks
 from twisted.internet.defer import returnValue
 
+from voltha.registry import registry
 from voltha.protos import third_party
 from voltha.protos.common_pb2 import OperStatus, ConnectStatus
 from common.utils.indexpool import IndexPool
 from voltha.extensions.omci.omci_me import *
 
+import voltha.adapters.adtran_olt.adtranolt_platform as platform
+
 _ = third_party
-_MAXIMUM_PORT = 128       # PON and UNI ports
+_MAXIMUM_PORT = 17        # Only one PON and UNI port at this time
 _ONU_REBOOT_MIN = 90      # IBONT 602 takes about 3 minutes
 _ONU_REBOOT_RETRY = 10
+BRDCM_DEFAULT_VLAN = 4091
 
 
 class AdtranOnuHandler(AdtranXPON):
@@ -58,17 +61,20 @@
 
         self._openomci = OMCI(self, adapter.omci_agent)
         self._in_sync_subscription = None
+        # TODO: Need to find a way to sync with OLT. It is part of OpenFlow Port number as well
+        self._onu_port_number = 0
+        self._pon_port_number = 1
+        self._port_number_pool = IndexPool(_MAXIMUM_PORT, 0)
 
         self._unis = dict()         # Port # -> UniPort
-        self._pon = None
+        self._pon = PonPort.create(self, self._pon_port_number)
         self._heartbeat = HeartBeat.create(self, device_id)
 
         self._deferred = None
         self._event_deferred = None
 
-        self._port_number_pool = IndexPool(_MAXIMUM_PORT, 1)
-
-        self._olt_created = False   # True if deprecated method of OLT creating DA is used
+        # Assume no XPON support unless we get an vont-ani/ont-ani/venet create
+        self.xpon_support = False    # xPON no longer available
 
     def __str__(self):
         return "AdtranOnuHandler: {}".format(self.device_id)
@@ -119,10 +125,6 @@
             # TODO: Anything else
 
     @property
-    def olt_created(self):
-        return self._olt_created    # ONU was created with deprecated 'child_device_detected' call
-
-    @property
     def openomci(self):
         return self._openomci
 
@@ -185,9 +187,7 @@
 
     def stop(self):
         assert not self._enabled, 'Stop should only be called if disabled'
-        #
-        # TODO: Perform common shutdown tasks here
-        #
+
         self._cancel_deferred()
 
         # Drop registration for adapter messages
@@ -225,10 +225,6 @@
             assert device.parent_id, 'Invalid Parent ID'
             assert device.proxy_address.device_id, 'Invalid Device ID'
 
-            if device.vlan:
-                # vlan non-zero if created via legacy method (not xPON).
-                self._olt_created = True
-
             # register for proxied messages right away
             self.proxy_address = device.proxy_address
             self.adapter_agent.register_for_proxied_messages(device.proxy_address)
@@ -243,23 +239,18 @@
             device.connect_status = ConnectStatus.UNKNOWN
 
             # Register physical ports.  Should have at least one of each
-            self._pon = PonPort.create(self, self._next_port_number)
             self.adapter_agent.add_port(device.id, self._pon.get_port())
 
-            if self._olt_created:
-                # vlan non-zero if created via legacy method (not xPON). Also
-                # Set a random serial number since not xPON based
+            def xpon_not_found():
+                if not self.xpon_support:
+                    # Start things up for this ONU Handler.
+                    self.enabled = True
 
-                uni_port = UniPort.create(self, self._next_port_number, device.vlan,
-                                          'deprecated', device.vlan, None)
-                self._unis[uni_port.port_number] = uni_port
-                self.adapter_agent.add_port(device.id, uni_port.get_port())
-
-                device.serial_number = uuid4().hex
-                uni_port.add_logical_port(device.vlan, subscriber_vlan=device.vlan)
-
-                # Start things up for this ONU Handler.
-                self.enabled = True
+            # Schedule xPON 'not found' startup for 10 seconds from now. We will
+            # easily get a vONT-ANI create within that time if xPON is being used
+            # as this is how we are initially launched and activated in the first
+            # place if xPON is in use.
+            reactor.callLater(10, xpon_not_found)
 
             # reference of uni_port is required when re-enabling the device if
             # it was disabled previously
@@ -339,179 +330,157 @@
         self.pm_metrics.update(pm_config)
 
     @inlineCallbacks
-    def update_flow_table(self, device, flows):
+    def update_flow_table(self, flows):
         #
         # 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.debug('bulk-flow-update', flows=flows)
 
         import voltha.core.flow_decomposer as fd
         from voltha.protos.openflow_13_pb2 import OFPXMC_OPENFLOW_BASIC
 
         def is_downstream(port):
-            return port == 100  # Need a better way
+            return port == self._pon_port_number
 
         def is_upstream(port):
             return not is_downstream(port)
 
-        omci = self.openomci.omci_cc
-
         for flow in flows:
-            _type = None
-            _port = None
-            _vlan_vid = None
-            _udp_dst = None
-            _udp_src = None
-            _ipv4_dst = None
-            _ipv4_src = None
-            _metadata = None
-            _output = None
-            _push_tpid = None
-            _field = None
-            _set_vlan_vid = None
-            self.log.info('bulk-flow-update', device_id=device.id, flow=flow)
+            match = {
+                'in_port': None,
+                # 'etype': None,            # These remaining key-value
+                # 'proto': None,            # pairs are set if found in the
+                # 'vlan_vid': None,         # flow information
+                # 'inner_vid': None,
+                # 'vlan_pcp': None,
+                # 'udp_dst': None,
+                # 'udp_src': None,
+                # 'ipv4_dst': None,
+                # 'ipv4_src': None,
+            }
+            actions = {
+                'out_port': None,
+                # 'push_tpid': None,        # These remaining key-value
+                # 'pop_vlan': None,  (bool) # pairs are set if found in the
+                # 'set_vlan_vid': None,     # flow information
+            }
+            self.log.debug('bulk-flow-update', flow=flow)
             try:
-                _in_port = fd.get_in_port(flow)
-                assert _in_port is not None
-
-                if is_downstream(_in_port):
-                    self.log.info('downstream-flow')
-                elif is_upstream(_in_port):
-                    self.log.info('upstream-flow')
-                else:
-                    raise Exception('port should be 1 or 2 by our convention')
-
                 _out_port = fd.get_out_port(flow)  # may be None
-                self.log.info('out-port', out_port=_out_port)
+                self.log.debug('out-port', out_port=_out_port)
 
                 for field in fd.get_ofb_fields(flow):
-                    if field.type == fd.ETH_TYPE:
-                        _type = field.eth_type
-                        self.log.info('field-type-eth-type',
-                                      eth_type=_type)
+                    if field.type == fd.IN_PORT:
+                        assert match['in_port'] is None, \
+                            'Only a single input port is supported'
+                        match['in_port'] = field.port
+                        self.log.debug('field-type-in-port', in_port=field.port)
+
+                    elif field.type == fd.ETH_TYPE:
+                        match['etype'] = field.eth_type
+                        self.log.debug('field-type-eth-type', eth_type=field.eth_type)
 
                     elif field.type == fd.IP_PROTO:
-                        _proto = field.ip_proto
-                        self.log.info('field-type-ip-proto',
-                                      ip_proto=_proto)
-
-                    elif field.type == fd.IN_PORT:
-                        _port = field.port
-                        self.log.info('field-type-in-port',
-                                      in_port=_port)
+                        match['proto'] = field.ip_proto
+                        self.log.debug('field-type-ip-proto', ip_proto=field.ip_proto)
 
                     elif field.type == fd.VLAN_VID:
-                        _vlan_vid = field.vlan_vid & 0xfff
-                        self.log.info('field-type-vlan-vid',
-                                      vlan=_vlan_vid)
+                        match['vlan_vid'] = field.vlan_vid & 0xfff
+                        self.log.debug('field-type-vlan-vid', vlan=field.vlan_vid & 0xfff)
 
                     elif field.type == fd.VLAN_PCP:
-                        _vlan_pcp = field.vlan_pcp
-                        self.log.info('field-type-vlan-pcp',
-                                      pcp=_vlan_pcp)
+                        match['vlan_pcp'] = field.vlan_pcp
+                        self.log.debug('field-type-vlan-pcp', pcp=field.vlan_pcp)
 
                     elif field.type == fd.UDP_DST:
-                        _udp_dst = field.udp_dst
-                        self.log.info('field-type-udp-dst',
-                                      udp_dst=_udp_dst)
+                        match['udp_dst'] = field.udp_dst
+                        self.log.debug('field-type-udp-dst', udp_dst=field.udp_dst)
 
                     elif field.type == fd.UDP_SRC:
-                        _udp_src = field.udp_src
-                        self.log.info('field-type-udp-src',
-                                      udp_src=_udp_src)
+                        match['udp_src'] = field.udp_src
+                        self.log.debug('field-type-udp-src', udp_src=field.udp_src)
 
                     elif field.type == fd.IPV4_DST:
-                        _ipv4_dst = field.ipv4_dst
-                        self.log.info('field-type-ipv4-dst',
-                                      ipv4_dst=_ipv4_dst)
+                        match['ipv4_dst'] = field.ipv4_dst
+                        self.log.debug('field-type-ipv4-dst', ipv4_dst=field.ipv4_dst)
 
                     elif field.type == fd.IPV4_SRC:
-                        _ipv4_src = field.ipv4_src
-                        self.log.info('field-type-ipv4-src',
-                                      ipv4_dst=_ipv4_src)
+                        match['ipv4_src'] = field.ipv4_src
+                        self.log.debug('field-type-ipv4-src', ipv4_dst=field.ipv4_src)
 
                     elif field.type == fd.METADATA:
-                        _metadata = field.table_metadata
-                        self.log.info('field-type-metadata',
-                                      metadata=_metadata)
+                        match['inner_vid'] = field.table_metadata
+                        self.log.debug('field-type-metadata', metadata=field.table_metadata)
 
                     else:
-                        raise NotImplementedError('field.type={}'.format(
-                            field.type))
+                        raise NotImplementedError('field.type={}'.format(field.type))
 
                 for action in fd.get_actions(flow):
-
                     if action.type == fd.OUTPUT:
-                        _output = action.output.port
-                        self.log.info('action-type-output',
-                                      output=_output, in_port=_in_port)
+                        actions['out_port'] = action.output.port
+                        self.log.debug('action-type-output', output=action.output.port)
 
                     elif action.type == fd.POP_VLAN:
-                        self.log.info('action-type-pop-vlan',
-                                      in_port=_in_port)
+                        actions['pop_vlan'] = True
+                        self.log.debug('action-type-pop-vlan')
 
                     elif action.type == fd.PUSH_VLAN:
-                        _push_tpid = action.push.ethertype
-                        self.log.info('action-type-push-vlan',
-                                 push_tpid=_push_tpid, in_port=_in_port)
+                        actions['push_tpid'] = action.push.ethertype
+                        self.log.debug('action-type-push-vlan',
+                                       push_tpid=action.push.ethertype)
+
                         if action.push.ethertype != 0x8100:
-                            self.log.error('unhandled-tpid',
-                                           ethertype=action.push.ethertype)
+                            self.log.error('unsupported-tpid',
+                                           ethertype=action.push.ethertype,
+                                           in_port=match['in_port'])
 
                     elif action.type == fd.SET_FIELD:
                         _field = action.set_field.field.ofb_field
-                        assert (action.set_field.field.oxm_class ==
-                                OFPXMC_OPENFLOW_BASIC)
-                        self.log.info('action-type-set-field',
-                                      field=_field, in_port=_in_port)
+                        self.log.debug('action-type-set-field', field=_field)
+                        assert (action.set_field.field.oxm_class == OFPXMC_OPENFLOW_BASIC)
+
                         if _field.type == fd.VLAN_VID:
-                            _set_vlan_vid = _field.vlan_vid & 0xfff
-                            self.log.info('set-field-type-valn-vid', _set_vlan_vid)
+                            actions['set_vlan_vid'] = _field.vlan_vid & 0xfff
+                            self.log.debug('set-field-type-vlan-vid', actions['set_vlan_vid'])
                         else:
                             self.log.error('unsupported-action-set-field-type',
-                                           field_type=_field.type)
+                                           field_type=_field.type,
+                                           in_port=match['in_port'])
                     else:
-                        self.log.error('unsupported-action-type',
-                                       action_type=action.type, in_port=_in_port)
-                #
-                # All flows created from ONU adapter should be OMCI based
-                #
-                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.log.error('unsupported-action-type', action_type=action.type,
+                                       in_port=match['in_port'])
 
-                    results = yield omci.send_delete_vlan_tagging_filter_data(0x2102)
+                assert match['in_port'] is not None, 'No input port specified'
+                assert actions['out_port'] is not None, 'No output port specified'
 
-                    # self.send_set_vlan_tagging_filter_data(0x2102, _set_vlan_vid)
-                    results = yield omci.send_create_vlan_tagging_filter_data(
-                                        0x2102,
-                                        _set_vlan_vid)
-
-                    results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(
-                                        0x202,
-                                        0x1000,
-                                        _set_vlan_vid)
-
-                    results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(
-                                        0x202,
-                                        8,
-                                        0,
-                                        0,
-                                        1,
-                                        8,
-                                        _set_vlan_vid)
-
-                    # Set AR - ExtendedVlanTaggingOperationConfigData
-                    #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
-                    '''
-                    results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(0x205, 8, 0, 0,
-                                                   
-                    '''
+                _is_upstream = is_upstream(match['in_port'])
 
             except Exception as e:
-                self.log.exception('failed-to-install-flow', e=e, flow=flow)
+                self.log.exception('failed-to-decode-flow', e=e)
+                continue
+
+            # Ignore untagged upstream etherType flows. These are trapped at the
+            # OLT and the default flows during initial OMCI service download will
+            # send them to the Default VLAN (4091) port for us
+            #
+            if _is_upstream and match.get('vlan_vid') is None \
+                    and match.get('etype') is not None:
+                continue
+
+            # Also ignore upstream untagged/priority tag that sets priority tag
+            # since that is already installed and any user-data flows for upstream
+            # priority tag data will be at a higher level.  Also should ignore the
+            # corresponding priority-tagged to priority-tagged flow as well.
+
+            if (match.get('vlan_vid') == 0 and action.get('set_vlan_vid') == 0) or \
+                    (match.get('vlan_vid') is None and action.get('set_vlan_vid') == 0
+                     and not _is_upstream):
+                continue
+
+            from omci.adtn_install_flow import AdtnInstallFlowTask
+            task = AdtnInstallFlowTask(self.openomci, self, match, action, _is_upstream)
+            self.openomci.onu_omci_device.task_runner.queue_task(task)
 
     @inlineCallbacks
     def reboot(self):
@@ -634,34 +603,8 @@
                                                                  self._pon.get_port())
             self._pon.enabled = False
 
-        # Send Uni Admin State Down
-
-        # ethernet_uni_entity_id = 0x101
-        # omci = self._handler.omci
-        # attributes = dict(
-        #     administrative_state=1  # - lock
-        # )
-        # frame = PptpEthernetUniFrame(
-        #     ethernet_uni_entity_id,  # Entity ID
-        #     attributes=attributes  # See above
-        # ).set()
-        # results = yield omci.send(frame)
-        #
-        # status = results.fields['omci_message'].fields['success_code']
-        # failed_attributes_mask = results.fields['omci_message'].fields['failed_attributes_mask']
-        # unsupported_attributes_mask = results.fields['omci_message'].fields['unsupported_attributes_mask']
-        # self.log.debug('set-pptp-ethernet-uni', status=status,
-        #                failed_attributes_mask=failed_attributes_mask,
-        #                unsupported_attributes_mask=unsupported_attributes_mask)
-
-
-        # Just updating the port status may be an option as well
-        # port.ofp_port.config = OFPPC_NO_RECV
-        # yield self.adapter_agent.update_logical_port(logical_device_id,
-        #                                             port)
         # Unregister for proxied message
-        self.adapter_agent.unregister_for_proxied_messages(
-            device.proxy_address)
+        self.adapter_agent.unregister_for_proxied_messages(device.proxy_address)
 
         # TODO:
         # 1) Remove all flows from the device
@@ -689,15 +632,11 @@
             # Re-enable the ports on that device
             self.adapter_agent.enable_all_ports(self.device_id)
 
-            # Refresh the port reference
-            # self.uni_port = self._get_uni_port()   deprecated
-
             # Add the pon port reference to the parent
             if self._pon is not None:
                 self._pon.enabled = True
                 self.adapter_agent.add_port_reference_to_parent(device.id,
                                                                 self._pon.get_port())
-
             # Update the connect status to REACHABLE
             device.connect_status = ConnectStatus.REACHABLE
             self.adapter_agent.update_device(device)
@@ -707,15 +646,10 @@
             self.logical_device_id = parent_device.parent_id
             assert self.logical_device_id, 'Invalid logical device ID'
 
-            if self.olt_created:
-                # vlan non-zero if created via legacy method (not xPON)
-                self.uni_port('deprecated').add_logical_port(device.vlan, device.vlan,
-                                                             subscriber_vlan=device.vlan)
-            else:
-                # reestablish logical ports for each UNI
-                for uni in self.uni_ports:
-                    self.adapter_agent.add_port(device.id, uni.get_port())
-                    uni.add_logical_port(uni.logical_port_number, subscriber_vlan=uni.subscriber_vlan)
+            # reestablish logical ports for each UNI
+            for uni in self.uni_ports:
+                self.adapter_agent.add_port(device.id, uni.get_port())
+                uni.add_logical_port(uni.logical_port_number, subscriber_vlan=uni.subscriber_vlan)
 
             device = self.adapter_agent.get_device(device.id)
             device.oper_status = OperStatus.ACTIVE
@@ -726,8 +660,7 @@
             self.adapter_agent.update_device(device)
 
             self.log.info('re-enabled', device_id=device.id)
-            self._pon._dev_info_loaded = False
-            self._bridge_initialized = False
+
         except Exception, e:
             self.log.exception('error-reenabling', e=e)
 
@@ -754,6 +687,8 @@
         :param ont_ani: (dict) new ONT-ani
         :return: (dict) Updated ONT-ani dictionary, None if item should be deleted
         """
+        self.xpon_support = True
+
         self.log.info('ont-ani-create', ont_ani=ont_ani)
         self.enabled = ont_ani['enabled']
 
@@ -771,6 +706,9 @@
         :param diffs: (dict) collection of items different in the update
         :return: (dict) Updated ONT-ani dictionary, None if item should be deleted
         """
+        if not self.xpon_support:
+            return
+
         valid_keys = ['enabled', 'mgnt-gemport-aes']  # Modify of these keys supported
 
         invalid_key = next((key for key in diffs.keys() if key not in valid_keys), None)
@@ -798,15 +736,22 @@
         :param ont_ani: (dict) ONT-ani to delete
         :return: (dict) None if item should be deleted
         """
+        if not self.xpon_support:
+            return
+
         # TODO: Is this ever called or is the iAdapter 'delete' called first?
         return None   # Implement in your OLT, if needed
 
     def on_vont_ani_create(self, vont_ani):
+        self.xpon_support = True
         self.log.info('vont-ani-create', vont_ani=vont_ani)
         # TODO: look up PON port and update 'upstream-channel-speed'
         return vont_ani   # Implement in your OLT, if needed
 
     def on_vont_ani_modify(self, vont_ani, update, diffs):
+        if not self.xpon_support:
+            return
+
         valid_keys = ['upstream-channel-speed']  # Modify of these keys supported
 
         invalid_key = next((key for key in diffs.keys() if key not in valid_keys), None)
@@ -822,9 +767,14 @@
         return update
 
     def on_vont_ani_delete(self, vont_ani):
+        if not self.xpon_support:
+            return
+
         return self.delete()
 
     def on_venet_create(self, venet):
+        self.xpon_support = True
+
         self.log.info('venet-create', venet=venet)
 
         # TODO: This first set is copied over from BroadCOM ONU. For testing, actual work
@@ -863,46 +813,109 @@
         # Start of actual work (what actually does something)
         # TODO: Clean this up.  Use looked up UNI
 
-        if self._olt_created:
-            uni_port = self.uni_port('deprecated')
+        # vlan non-zero if created via legacy method (not xPON). Also
+        # Set a random serial number since not xPON based
 
-        else:
-            # vlan non-zero if created via legacy method (not xPON). Also
-            # Set a random serial number since not xPON based
+        ofp_port_no, subscriber_vlan, untagged_vlan = UniPort.decode_venet(venet)
 
-            device = self.adapter_agent.get_device(self.device_id)
-            ofp_port_no, subscriber_vlan, untagged_vlan = UniPort.decode_venet(venet)
+        self._add_uni_port(self, venet['name'], ofp_port_no, subscriber_vlan,
+                           untagged_vlan, venet['enabled'])
+        return venet
 
-            uni_port = UniPort.create(self, venet['name'],
-                                      self._next_port_number,
-                                      ofp_port_no,
-                                      subscriber_vlan,
-                                      untagged_vlan)
+    # SEBA - Below is used by xPON mode
+    def _add_uni_port(self, port_name, ofp_port_no, subscriber_vlan, untagged_vlan, enable):
+        uni_port = UniPort.create(self, port_name,
+                                  self._onu_port_number,  # TODO: self._next_port_number,
+                                  ofp_port_no,
+                                  subscriber_vlan,
+                                  untagged_vlan)
 
-            self._unis[uni_port.port_number] = uni_port
-            self.adapter_agent.add_port(device.id, uni_port.get_port())
+        device = self.adapter_agent.get_device(self.device_id)
+        self.adapter_agent.add_port(device.id, uni_port.get_port())
 
-            # If the PON has already synchronized, add the logical port now
-            # since we know we have been activated
+        self._unis[uni_port.port_number] = uni_port
 
-            if self._pon is not None and self.openomci.connected:
-                uni_port.add_logical_port(ofp_port_no, subscriber_vlan=subscriber_vlan)
+        # If the PON has already synchronized, add the logical port now
+        # since we know we have been activated
+
+        if self._pon is not None and self.openomci.connected:
+            uni_port.add_logical_port(ofp_port_no, subscriber_vlan=subscriber_vlan)
 
         # TODO: Next is just for debugging to see what this call returns after
         #       we add a UNI
         # existing_uni_ports = self.adapter_agent.get_ports(onu_device.parent_id, Port.ETHERNET_UNI)
 
-        uni_port.enabled = venet['enabled']
+        uni_port.enabled = enable
 
-        return venet
+    def add_uni_ports(self):
+        """ Called after in-sync achieved and not in xPON mode"""
+        # TODO: Should this be moved to the omci.py module for this ONU?
+
+        # This is only for working WITHOUT xPON
+        assert not self.xpon_support
+        pptp_entities = self.openomci.onu_omci_device.configuration.pptp_entities
+
+        device = self.adapter_agent.get_device(self.device_id)
+        subscriber_vlan = device.vlan
+        untagged_vlan = BRDCM_DEFAULT_VLAN          # TODO: Need a better way to define this
+
+        for entity_id, pptp in pptp_entities.items():
+            intf_id = self.proxy_address.channel_id
+            onu_id = self.proxy_address.onu_id
+
+            uni_no_start = platform.mk_uni_port_num(intf_id, onu_id)
+
+            working_port = self._next_port_number
+            uni_no = uni_no_start + working_port        # OpenFlow port number
+            uni_name = "uni-{}".format(uni_no)
+
+            mac_bridge_port_num = working_port + 1
+
+            self.log.debug('live-port-number-ready', uni_no=uni_no, uni_name=uni_name)
+
+            uni_port = UniPort.create(self, uni_name, uni_no, uni_name,
+                                      subscriber_vlan, untagged_vlan)
+
+            uni_port.entity_id = entity_id
+            uni_port.enabled = True
+            uni_port.mac_bridge_port_num = mac_bridge_port_num
+            uni_port.add_logical_port(uni_port.port_number, subscriber_vlan=subscriber_vlan)
+
+            self.log.debug("created-uni-port", uni=uni_port)
+
+            self.adapter_agent.add_port(device.id, uni_port.get_port())
+            parent_device = self.adapter_agent.get_device(device.parent_id)
+
+            parent_adapter_agent = registry('adapter_loader').get_agent(parent_device.adapter)
+            if parent_adapter_agent is None:
+                self.log.error('olt-adapter-agent-could-not-be-retrieved')
+
+            parent_adapter_agent.add_port(device.parent_id, uni_port.get_port())
+
+            self._unis[uni_port.port_number] = uni_port
+
+            # TODO: this should be in the PonPort class
+            pon_port = self._pon.get_port()
+            self.adapter_agent.delete_port_reference_from_parent(self.device_id,
+                                                                 pon_port)
+            # Find index where this ONU peer is (should almost always be zero)
+            d = [i for i, e in enumerate(pon_port.peers) if
+                 e.port_no == intf_id and e.device_id == device.parent_id]
+
+            if len(d) > 0:
+                pon_port.peers[d[0]].port_no = uni_port.port_number
+                self.adapter_agent.add_port_reference_to_parent(self.device_id,
+                                                                pon_port)
+            self.adapter_agent.update_device(device)
+
+            # TODO: only one uni/pptp for now. flow bug in openolt
 
     def on_venet_modify(self, venet, update, diffs):
-        # Look up the associated UNI port
+        if not self.xpon_support:
+            return
 
-        if self._olt_created:
-            uni_port = self.uni_port('deprecated')
-        else:
-            uni_port = self.uni_port(venet['name'])
+        # Look up the associated UNI port
+        uni_port = self.uni_port(venet['name'])
 
         if uni_port is not None:
             valid_keys = ['enabled']  # Modify of these keys supported
@@ -920,12 +933,11 @@
         return update
 
     def on_venet_delete(self, venet):
-        # Look up the associated UNI port
+        if not self.xpon_support:
+            return
 
-        if self._olt_created:
-            uni_port = self.uni_port('deprecated')
-        else:
-            uni_port = self.uni_port(venet['name'])
+        # Look up the associated UNI port
+        uni_port = self.uni_port(venet['name'])
 
         if uni_port is not None:
             port_no = uni_port.port_number
@@ -1158,7 +1170,7 @@
                 if msg[IN_SYNC_KEY]:
                     # Do not proceed if we have not got our vENET information yet.
 
-                    if len(self.uni_ports) > 0:
+                    if len(self.uni_ports) > 0 or not self.xpon_support:
                         # Drop subscription....
                         insync, self._in_sync_subscription = self._in_sync_subscription, None
 
@@ -1170,10 +1182,13 @@
                         # vENET information is created. Once xPON is removed, we need to create
                         # them from the information provided from the MIB upload UNI-G and other
                         # UNI related MEs.
-
-                        for uni in self.uni_ports:
-                            uni.add_logical_port(None, None)
+                        if not self.xpon_support:
+                            self.add_uni_ports()
+                        else:
+                            for uni in self.uni_ports:
+                                uni.add_logical_port(None, None)
                     else:
+                        # SEBA - drop this one once xPON deprecated
                         self._deferred = reactor.callLater(5, self.in_sync_handler, _topic, msg)
 
             except Exception as e:
diff --git a/voltha/adapters/adtran_onu/heartbeat.py b/voltha/adapters/adtran_onu/heartbeat.py
index f0efe30..4a7ab1f 100644
--- a/voltha/adapters/adtran_onu/heartbeat.py
+++ b/voltha/adapters/adtran_onu/heartbeat.py
@@ -69,14 +69,10 @@
 
     @property
     def check_item(self):
-        # return 'vendor_id' if self._handler.olt_created else 'serial_number'
-        return 'vendor_id' if self._handler.olt_created else 'vendor_id'
+        return 'vendor_id'
 
     @property
     def check_value(self):
-        if self._handler.olt_created:
-            return 'ADTN'
-
         # device = self._handler.adapter_agent.get_device(self._device_id)
         # return device.serial_number
         return 'ADTN'
diff --git a/voltha/adapters/adtran_onu/omci/adtn_install_flow.py b/voltha/adapters/adtran_onu/omci/adtn_install_flow.py
new file mode 100644
index 0000000..f3e2cee
--- /dev/null
+++ b/voltha/adapters/adtran_onu/omci/adtn_install_flow.py
@@ -0,0 +1,309 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks, returnValue, TimeoutError, failure
+from voltha.extensions.omci.omci_me import *
+from voltha.extensions.omci.tasks.task import Task
+from voltha.extensions.omci.omci_defs import *
+
+OP = EntityOperations
+RC = ReasonCodes
+
+
+class ServiceDownloadFailure(Exception):
+    """
+    This error is raised by default when the download fails
+    """
+
+
+class ServiceResourcesFailure(Exception):
+    """
+    This error is raised by when one or more resources required is not available
+    """
+
+
+class AdtnInstallFlowTask(Task):
+    """
+    OpenOMCI MIB Download Example - Service specific
+
+    This task takes the legacy OMCI 'script' for provisioning the Adtran ONU
+    and converts it to run as a Task on the OpenOMCI Task runner.  This is
+    in order to begin to decompose service instantiation in preparation for
+    Technology Profile work.
+
+    Once technology profiles are ready, some of this task may hang around or
+    be moved into OpenOMCI if there are any very common settings/configs to do
+    for any profile that may be provided in the v2.0 release
+
+    Currently, the only service tech profiles expected by v2.0 will be for AT&T
+    residential data service and DT residential data service.
+    """
+    task_priority = Task.DEFAULT_PRIORITY + 10
+    default_tpid = 0x8100
+    default_gem_payload = 1518
+    BRDCM_DEFAULT_VLAN = 4091
+
+    name = "ADTRAN MIB Install Flow Task"
+
+    def __init__(self, omci_agent, handler, match, action, is_upstream):
+        """
+        Class initialization
+
+        :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
+        :param handler: (AdtranOnuHandler) ONU Handler
+        :param match: (dict) Flow match rules
+        :param action: (dict) Flow action rules
+        :param is_upstream: (bool) True if upstream flow is being installed
+        """
+        super(AdtnInstallFlowTask, self).__init__(AdtnInstallFlowTask.name,
+                                                  omci_agent,
+                                                  handler.device_id,
+                                                  priority=AdtnInstallFlowTask.task_priority)
+        self._handler = handler
+        self._onu_device = omci_agent.get_device(handler.device_id)
+        self._local_deferred = None
+
+        self._match = match
+        self._action = action
+
+        # TODO: Cleanup below that is not needed
+        self._vlan_tcis_1 = 0x900
+        self._input_tpid = AdtnInstallFlowTask.default_tpid
+        self._output_tpid = AdtnInstallFlowTask.default_tpid
+
+        if self._handler.xpon_support:
+            device = self._handler.adapter_agent.get_device(self.device_id)
+            self._cvid = device.vlan
+        else:
+            # TODO: TCIS below is just a test, may need 0x900...as in the xPON mode
+            self._vlan_tcis_1 = AdtnInstallFlowTask.BRDCM_DEFAULT_VLAN
+            self._cvid = AdtnInstallFlowTask.BRDCM_DEFAULT_VLAN
+
+        # Entity IDs. IDs with values can probably be most anything for most ONUs,
+        #             IDs set to None are discovered/set
+        #
+        # TODO: Probably need to store many of these in the appropriate object (UNI, PON,...)
+        #
+        self._ieee_mapper_service_profile_entity_id = 0x100
+        self._gal_enet_profile_entity_id = 0x100
+
+        # Next to are specific
+        self._ethernet_uni_entity_id = self._handler.uni_ports[0].entity_id
+        self._vlan_config_entity_id = self._vlan_tcis_1
+
+    def cancel_deferred(self):
+        super(AdtnInstallFlowTask, self).cancel_deferred()
+
+        d, self._local_deferred = self._local_deferred, None
+        try:
+            if d is not None and not d.called:
+                d.cancel()
+        except:
+            pass
+
+    def start(self):
+        """
+        Start the flow installation
+        """
+        super(AdtnInstallFlowTask, self).start()
+        self._local_deferred = reactor.callLater(0, self.perform_flow_install)
+
+    def stop(self):
+        """
+        Shutdown flow install task
+        """
+        self.log.debug('stopping')
+
+        self.cancel_deferred()
+        super(AdtnInstallFlowTask, self).stop()
+
+    def check_status_and_state(self, results, operation=''):
+        """
+        Check the results of an OMCI response.  An exception is thrown
+        if the task was cancelled or an error was detected.
+
+        :param results: (OmciFrame) OMCI Response frame
+        :param operation: (str) what operation was being performed
+        :return: True if successful, False if the entity existed (already created)
+        """
+        omci_msg = results.fields['omci_message'].fields
+        status = omci_msg['success_code']
+        error_mask = omci_msg.get('parameter_error_attributes_mask', 'n/a')
+        failed_mask = omci_msg.get('failed_attributes_mask', 'n/a')
+        unsupported_mask = omci_msg.get('unsupported_attributes_mask', 'n/a')
+
+        self.log.debug(operation, status=status, error_mask=error_mask,
+                       failed_mask=failed_mask, unsupported_mask=unsupported_mask)
+
+        if status == RC.Success:
+            self.strobe_watchdog()
+            return True
+
+        elif status == RC.InstanceExists:
+            return False
+
+        raise ServiceDownloadFailure('{} failed with a status of {}, error_mask: {}, failed_mask: {}, unsupported_mask: {}'
+                                     .format(operation, status, error_mask, failed_mask, unsupported_mask))
+
+    @inlineCallbacks
+    def perform_flow_install(self):
+        """
+        Send the commands to configure the flow
+        """
+        self.log.info('perform-service-download')
+
+        if self._handler.xpon_support:
+            self.deferred.callback('flow-install-nop')  # xPON mode does not need this
+
+        def resources_available():
+            # TODO: Rework for non-xpon mode
+            return (len(self._handler.uni_ports) > 0 and
+                    len(self._handler.pon_port.tconts) and
+                    len(self._handler.pon_port.gem_ports) and
+                    self._action.get('set_vlan_vid') is not None)
+
+        if self._handler.enabled and resources_available():
+            omci = self._onu_device.omci_cc
+            try:
+                # TODO: make this a member of the onu gem port or the uni port
+                _mac_bridge_service_profile_entity_id = 0x201
+                _mac_bridge_port_ani_entity_id = 0x2102  # TODO: can we just use the entity id from the anis list?
+                _set_vlan_vid = self._action['set_vlan_vid']
+
+                # Delete bridge ani side vlan filter
+                msg = VlanTaggingFilterDataFrame(_mac_bridge_port_ani_entity_id)
+                frame = msg.delete()
+
+                results = yield omci.send(frame)
+                self.check_status_and_state(results, 'flow-delete-vlan-tagging-filter-data')
+
+                # Re-Create bridge ani side vlan filter
+                msg = VlanTaggingFilterDataFrame(
+                        _mac_bridge_port_ani_entity_id,  # Entity ID
+                        vlan_tcis=[_set_vlan_vid],  # VLAN IDs
+                        forward_operation=0x10
+                )
+                frame = msg.create()
+
+                results = yield omci.send(frame)
+                self.check_status_and_state(results, 'flow-create-vlan-tagging-filter-data')
+
+                # Update uni side extended vlan filter
+                # filter for untagged
+                # probably for eapol
+                # TODO: magic 0x1000 / 4096?
+                # TODO: lots of magic
+                attributes = dict(
+                        received_frame_vlan_tagging_operation_table=
+                        VlanTaggingOperation(
+                                filter_outer_priority=15,
+                                filter_outer_vid=4096,
+                                filter_outer_tpid_de=0,
+
+                                filter_inner_priority=15,
+                                filter_inner_vid=4096,
+                                filter_inner_tpid_de=0,
+                                filter_ether_type=0,
+
+                                treatment_tags_to_remove=0,
+                                treatment_outer_priority=15,
+                                treatment_outer_vid=0,
+                                treatment_outer_tpid_de=0,
+
+                                treatment_inner_priority=0,
+                                treatment_inner_vid=_set_vlan_vid,
+                                treatment_inner_tpid_de=4
+                        )
+                )
+                # TODO: Move this to a task
+                msg = ExtendedVlanTaggingOperationConfigurationDataFrame(
+                        _mac_bridge_service_profile_entity_id,  # Bridge Entity ID
+                        attributes=attributes  # See above
+                )
+                frame = msg.set()
+
+                results = yield omci.send(frame)
+                self.check_status_and_state(results,
+                                            'flow-set-ext-vlan-tagging-op-config-data-untagged')
+
+                # Update uni side extended vlan filter
+                # filter for vlan 0
+                # TODO: lots of magic
+                attributes = dict(
+                        received_frame_vlan_tagging_operation_table=
+                        VlanTaggingOperation(
+                                filter_outer_priority=15,
+                                # This entry is not a double-tag rule
+                                filter_outer_vid=4096,  # Do not filter on the outer VID value
+                                filter_outer_tpid_de=0,
+                                # Do not filter on the outer TPID field
+
+                                filter_inner_priority=8,  # Filter on inner vlan
+                                filter_inner_vid=0x0,  # Look for vlan 0
+                                filter_inner_tpid_de=0,  # Do not filter on inner TPID field
+                                filter_ether_type=0,  # Do not filter on EtherType
+
+                                treatment_tags_to_remove=1,
+                                treatment_outer_priority=15,
+                                treatment_outer_vid=0,
+                                treatment_outer_tpid_de=0,
+
+                                treatment_inner_priority=8,
+                                # Add an inner tag and insert this value as the priority
+                                treatment_inner_vid=_set_vlan_vid,
+                                # use this value as the VID in the inner VLAN tag
+                                treatment_inner_tpid_de=4,  # set TPID
+                        )
+                )
+                msg = ExtendedVlanTaggingOperationConfigurationDataFrame(
+                        _mac_bridge_service_profile_entity_id,  # Bridge Entity ID
+                        attributes=attributes  # See above
+                )
+                frame = msg.set()
+
+                results = yield omci.send(frame)
+                self.check_status_and_state(results,
+                                            'flow-set-ext-vlan-tagging-op-config-data-zero-tagged')
+                self.deferred.callback('flow-install-success')
+
+            except Exception as e:
+                # TODO: Better context info for this exception output...
+                self.log.exception('failed-to-install-flow', e=e)
+                self.deferred.errback(failure.Failure(e))
+
+        else:
+            # TODO: Provide better error reason, what was missing...
+            e = ServiceResourcesFailure('Required resources are not available')
+            self.deferred.errback(failure.Failure(e))
+
+    def check_status_and_state(self, result, operation=''):
+        from voltha.extensions.omci.omci_defs import ReasonCodes
+        self.log.debug('function-entry')
+        omci_msg = result.fields['omci_message'].fields
+        status = omci_msg['success_code']
+        error_mask = omci_msg.get('parameter_error_attributes_mask', 'n/a')
+        failed_mask = omci_msg.get('failed_attributes_mask', 'n/a')
+        unsupported_mask = omci_msg.get('unsupported_attributes_mask', 'n/a')
+
+        self.log.debug("OMCI Result:", operation, omci_msg=omci_msg, status=status,
+                       error_mask=error_mask,
+                       failed_mask=failed_mask, unsupported_mask=unsupported_mask)
+
+        if status == ReasonCodes.Success:
+            return True
+
+        elif status == ReasonCodes.InstanceExists:
+            return False
diff --git a/voltha/adapters/adtran_onu/omci/adtn_mib_download_task.py b/voltha/adapters/adtran_onu/omci/adtn_mib_download_task.py
index e782913..6c853e1 100644
--- a/voltha/adapters/adtran_onu/omci/adtn_mib_download_task.py
+++ b/voltha/adapters/adtran_onu/omci/adtn_mib_download_task.py
@@ -80,15 +80,12 @@
         self._uni_port_num = 0  # TODO Both port numbers are the same, is this correct?  See MacBridgePortConfigurationDataFrame
 
         self._vlan_tcis_1 = 0x900
-        self._input_tpid = AdtnMibDownloadTask.default_tpid
-        self._output_tpid = AdtnMibDownloadTask.default_tpid
-
-        device = self._handler.adapter_agent.get_device(self.device_id)
-        self._cvid = device.vlan
 
         # Entity IDs. IDs with values can probably be most anything for most ONUs,
         #             IDs set to None are discovered/set
-
+        #
+        # TODO: Probably need to store many of these in the appropriate object (UNI, PON,...)
+        #
         self._mac_bridge_service_profile_entity_id = 0x100
         self._ieee_mapper_service_profile_entity_id = 0x100
         self._mac_bridge_port_ani_entity_id = 0x100
@@ -157,20 +154,18 @@
         """
         Send the commands to minimally configure the PON, Bridge, and
         UNI ports for this device. The application of any service flows
-        and other characteristics are done as needed.
+        and other characteristics are done once resources (gem-ports, tconts, ...)
+        have been defined.
         """
-        self.log.info('perform-download')
+        self.log.info('perform-initial-download')
 
         device = self._handler.adapter_agent.get_device(self.device_id)
 
         def resources_available():
-            return (device.vlan > 0 and
-                    len(self._handler.uni_ports) > 0 and
-                    len(self._handler.pon_port.tconts) and
-                    len(self._handler.pon_port.gem_ports))
+            return len(self._handler.uni_ports) > 0
 
         if self._handler.enabled and resources_available():
-            device.reason = 'Performing OMCI Download'
+            device.reason = 'Performing Initial OMCI Download'
             self._handler.adapter_agent.update_device(device)
 
             try:
@@ -182,16 +177,10 @@
                 # Provision the initial bridge configuration
                 yield self.perform_initial_bridge_setup()
 
-                # And not all the service specific work
-                yield self.perform_service_specific_steps()
-
-                # And re-enable the UNIs if needed
-                yield self.enable_unis(self._handler.uni_ports, False)
-
                 # If here, we are done
                 device = self._handler.adapter_agent.get_device(self.device_id)
 
-                device.reason = ''
+                device.reason = 'Initial OMCI Download Complete'
                 self._handler.adapter_agent.update_device(device)
                 self.deferred.callback('TODO: What should we return to the caller?')
 
@@ -200,7 +189,7 @@
 
         else:
             # TODO: Provide better error reason, what was missing...
-            e = MibResourcesFailure('Required resources are not available')
+            e = MibResourcesFailure('ONU is not enabled')
             self.deferred.errback(failure.Failure(e))
 
     @inlineCallbacks
@@ -336,215 +325,6 @@
         returnValue(None)
 
     @inlineCallbacks
-    def perform_service_specific_steps(self):
-        omci_cc = self._onu_device.omci_cc
-        frame = None
-
-        try:
-            ################################################################################
-            # TCONTS
-            #
-            #  EntityID will be referenced by:
-            #            - GemPortNetworkCtp
-            #  References:
-            #            - ONU created TCONT (created on ONU startup)
-
-            tcont_idents = self._onu_device.query_mib(Tcont.class_id)
-            self.log.debug('tcont-idents', tcont_idents=tcont_idents)
-
-            for tcont in self._handler.pon_port.tconts.itervalues():
-                free_entity_id = next((k for k, v in tcont_idents.items()
-                                       if isinstance(k, int) and
-                                       v.get('attributes', {}).get('alloc_id', 0) == 0xFFFF), None)
-                if free_entity_id is None:
-                    self.log.error('no-available-tconts')
-                    break
-                # TODO: Need to restore on failure.  Need to check status/results
-                yield tcont.add_to_hardware(omci_cc, free_entity_id)
-
-            ################################################################################
-            # GEMS  (GemPortNetworkCtp and GemInterworkingTp)
-            #
-            #  For both of these MEs, the entity_id is the GEM Port ID. The entity id of the
-            #  GemInterworkingTp ME could be different since it has an attribute to specify
-            #  the GemPortNetworkCtp entity id.
-            #
-            #  TODO: In the GEM Port routine to add, it has a hardcoded upstream TM ID of 0x8000
-            #        for the GemPortNetworkCtp ME
-            #
-            #  GemPortNetworkCtp
-            #    EntityID will be referenced by:
-            #              - GemInterworkingTp
-            #    References:
-            #              - TCONT
-            #              - Hardcoded upstream TM Entity ID
-            #              - (Possibly in Future) Upstream Traffic descriptor profile pointer
-            #
-            #  GemInterworkingTp
-            #    EntityID will be referenced by:
-            #              - Ieee8021pMapperServiceProfile
-            #    References:
-            #              - GemPortNetworkCtp
-            #              - Ieee8021pMapperServiceProfile
-            #              - GalEthernetProfile
-            #
-
-            for gem_port in self._handler.pon_port.gem_ports.itervalues():
-                tcont = gem_port.tcont
-                if tcont is None:
-                    self.log.error('unknown-tcont-reference', gem_id=gem_port.gem_id)
-                    continue
-                # TODO: Need to restore on failure.  Need to check status/results
-                yield gem_port.add_to_hardware(omci_cc,
-                                               tcont.entity_id,
-                                               self._ieee_mapper_service_profile_entity_id,
-                                               self._gal_enet_profile_entity_id)
-
-            ################################################################################
-            # Update the IEEE 802.1p Mapper Service Profile config
-            #
-            #  EntityID was created prior to this call. This is a set
-            #
-            #  References:
-            #            - Gem Interworking TPs are set here
-            #
-            # TODO: All p-bits currently go to the one and only GEMPORT ID for now
-            gem_ports = self._handler.pon_port.gem_ports
-            gem_entity_ids = [gem_port.entity_id for _, gem_port in gem_ports.items()] \
-                if len(gem_ports) else [OmciNullPointer]
-
-            frame = Ieee8021pMapperServiceProfileFrame(
-                self._ieee_mapper_service_profile_entity_id,  # 802.1p mapper Service Mapper Profile ID
-                interwork_tp_pointers=gem_entity_ids          # Interworking TP IDs
-            ).set()
-            results = yield omci_cc.send(frame)
-            self.check_status_and_state(results, 'set-8021p-mapper-service-profile')
-
-            ################################################################################
-            # Create Extended VLAN Tagging Operation config (PON-side)
-            #
-            #  EntityID relates to the VLAN TCIS
-            #  References:
-            #            - VLAN TCIS from previously created VLAN Tagging filter data
-            #            - PPTP Ethernet UNI
-            #
-            # TODO: add entry here for additional UNI interfaces
-
-            attributes = dict(
-                association_type=2,                                 # Assoc Type, PPTP Ethernet UNI
-                associated_me_pointer=self._ethernet_uni_entity_id  # Assoc ME, PPTP Entity Id
-            )
-
-            frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
-                self._vlan_config_entity_id,
-                attributes=attributes
-            ).create()
-            results = yield omci_cc.send(frame)
-            self.check_status_and_state(results, 'create-extended-vlan-tagging-operation-configuration-data')
-
-            ################################################################################
-            # Update Extended VLAN Tagging Operation Config Data
-            #
-            # Specifies the TPIDs in use and that operations in the downstream direction are
-            # inverse to the operations in the upstream direction
-            # TODO: Downstream mode may need to be modified once we work more on the flow rules
-
-            attributes = dict(
-                input_tpid=self._input_tpid,    # input TPID
-                output_tpid=self._output_tpid,  # output TPID
-                downstream_mode=0,              # inverse of upstream
-            )
-            frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
-                self._vlan_config_entity_id,
-                attributes=attributes
-            ).set()
-            results = yield omci_cc.send(frame)
-            self.check_status_and_state(results, 'set-extended-vlan-tagging-operation-configuration-data')
-
-            ################################################################################
-            # Update Extended VLAN Tagging Operation Config Data
-            #
-            # parameters: Entity Id ( 0x900), Filter Inner Vlan Id(0x1000-4096,do not filter on Inner vid,
-            #             Treatment Inner Vlan Id : 2
-
-            attributes = dict(
-                received_frame_vlan_tagging_operation_table=
-                VlanTaggingOperation(
-                    filter_outer_priority=15,  # This entry is not a double-tag rule
-                    filter_outer_vid=4096,     # Do not filter on the outer VID value
-                    filter_outer_tpid_de=0,    # Do not filter on the outer TPID field
-
-                    filter_inner_priority=15,  # This is a no-tag rule, ignore all other VLAN tag filter fields
-                    filter_inner_vid=0x1000,   # Do not filter on the inner VID
-                    filter_inner_tpid_de=0,    # Do not filter on inner TPID field
-
-                    filter_ether_type=0,         # Do not filter on EtherType
-                    treatment_tags_to_remove=0,  # Remove 0 tags
-
-                    treatment_outer_priority=15,  # Do not add an outer tag
-                    treatment_outer_vid=0,        # n/a
-                    treatment_outer_tpid_de=0,    # n/a
-
-                    treatment_inner_priority=0,      # Add an inner tag and insert this value as the priority
-                    treatment_inner_vid=self._cvid,  # use this value as the VID in the inner VLAN tag
-                    treatment_inner_tpid_de=4,       # set TPID
-                )
-            )
-            frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
-                self._vlan_config_entity_id,  # Entity ID
-                attributes=attributes         # See above
-            ).set()
-            results = yield omci_cc.send(frame)
-            self.check_status_and_state(results, 'set-extended-vlan-tagging-operation-configuration-data-untagged')
-
-            ################################################################################
-            ################################################################################
-            ################################################################################
-            # BP: This is for AT&T RG's                #
-            #   TODO: CB: NOTE: TRY THIS ONCE OTHER SEQUENCES WORK
-            #
-            # Set AR - ExtendedVlanTaggingOperationConfigData
-            #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
-            # results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(
-            #                                 0x900,  # Entity ID
-            #                                 8,      # Filter Inner Priority, do not filter on Inner Priority
-            #                                 0,    # Filter Inner VID, this will be 0 in CORD
-            #                                 0,      # Filter Inner TPID DE
-            #                                 1,      # Treatment tags, number of tags to remove
-            #                                 8,      # Treatment inner priority, copy Inner Priority
-            #                                 2)   # Treatment inner VID, this will be 2 in CORD
-            # Set AR - ExtendedVlanTaggingOperationConfigData
-            #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
-            # results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(
-            #                                 0x200,  # Entity ID
-            #                                 8,      # Filter Inner Priority
-            #                                 0,      # Filter Inner VID
-            #                                 0,      # Filter Inner TPID DE
-            #                                 1,      # Treatment tags to remove
-            #                                 8,      # Treatment inner priority
-            #                                 cvid)   # Treatment inner VID
-            # Set AR - ExtendedVlanTaggingOperationConfigData
-            #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to untagged pkts - c-vid
-            # results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(
-            #                                0x100,   # Entity ID            BP: Oldvalue 0x202
-            #                                0x1000,  # Filter Inner VID     BP: Oldvalue 0x1000
-            #                                cvid)    # Treatment inner VID  BP: cvid
-            # success = results.fields['omci_message'].fields['success_code'] == 0
-            # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
-
-            ###############################################################################
-
-        except TimeoutError as e:
-            self.log.warn('rx-timeout-2', frame=frame)
-            raise
-
-        except Exception as e:
-            self.log.exception('omci-setup-2', e=e)
-            raise
-
-        returnValue(None)
-
-    @inlineCallbacks
     def enable_unis(self, unis, force_lock):
         """
         Lock or unlock one or more UNI ports
diff --git a/voltha/adapters/adtran_onu/omci/adtn_service_download_task.py b/voltha/adapters/adtran_onu/omci/adtn_service_download_task.py
new file mode 100644
index 0000000..e62cb6e
--- /dev/null
+++ b/voltha/adapters/adtran_onu/omci/adtn_service_download_task.py
@@ -0,0 +1,459 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks, returnValue, TimeoutError, failure
+from voltha.extensions.omci.omci_me import *
+from voltha.extensions.omci.tasks.task import Task
+from voltha.extensions.omci.omci_defs import *
+
+OP = EntityOperations
+RC = ReasonCodes
+
+
+class ServiceDownloadFailure(Exception):
+    """
+    This error is raised by default when the download fails
+    """
+
+
+class ServiceResourcesFailure(Exception):
+    """
+    This error is raised by when one or more resources required is not available
+    """
+
+
+class AdtnServiceDownloadTask(Task):
+    """
+    OpenOMCI MIB Download Example - Service specific
+
+    This task takes the legacy OMCI 'script' for provisioning the Adtran ONU
+    and converts it to run as a Task on the OpenOMCI Task runner.  This is
+    in order to begin to decompose service instantiation in preparation for
+    Technology Profile work.
+
+    Once technology profiles are ready, some of this task may hang around or
+    be moved into OpenOMCI if there are any very common settings/configs to do
+    for any profile that may be provided in the v2.0 release
+
+    Currently, the only service tech profiles expected by v2.0 will be for AT&T
+    residential data service and DT residential data service.
+    """
+    task_priority = Task.DEFAULT_PRIORITY + 10
+    default_tpid = 0x8100
+    default_gem_payload = 1518
+    BRDCM_DEFAULT_VLAN = 4091
+
+    name = "ADTRAN MIB Download Example Task"
+
+    def __init__(self, omci_agent, handler):
+        """
+        Class initialization
+
+        :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
+        :param device_id: (str) ONU Device ID
+        """
+        super(AdtnServiceDownloadTask, self).__init__(AdtnServiceDownloadTask.name,
+                                                      omci_agent,
+                                                      handler.device_id,
+                                                      priority=AdtnServiceDownloadTask.task_priority)
+        self._handler = handler
+        self._onu_device = omci_agent.get_device(handler.device_id)
+        self._local_deferred = None
+
+        self._vlan_tcis_1 = 0x900
+        self._input_tpid = AdtnServiceDownloadTask.default_tpid
+        self._output_tpid = AdtnServiceDownloadTask.default_tpid
+
+        if self._handler.xpon_support:
+            device = self._handler.adapter_agent.get_device(self.device_id)
+            self._cvid = device.vlan
+        else:
+            # TODO: TCIS below is just a test, may need 0x900...as in the xPON mode
+            self._vlan_tcis_1 = AdtnServiceDownloadTask.BRDCM_DEFAULT_VLAN
+            self._cvid = AdtnServiceDownloadTask.BRDCM_DEFAULT_VLAN
+
+        # Entity IDs. IDs with values can probably be most anything for most ONUs,
+        #             IDs set to None are discovered/set
+        #
+        # TODO: Probably need to store many of these in the appropriate object (UNI, PON,...)
+        #
+        self._ieee_mapper_service_profile_entity_id = 0x100
+        self._gal_enet_profile_entity_id = 0x100
+
+        # Next to are specific
+        self._ethernet_uni_entity_id = self._handler.uni_ports[0].entity_id
+        self._vlan_config_entity_id = self._vlan_tcis_1
+
+    def cancel_deferred(self):
+        super(AdtnServiceDownloadTask, self).cancel_deferred()
+
+        d, self._local_deferred = self._local_deferred, None
+        try:
+            if d is not None and not d.called:
+                d.cancel()
+        except:
+            pass
+
+    def start(self):
+        """
+        Start the MIB Service Download
+        """
+        super(AdtnServiceDownloadTask, self).start()
+        self._local_deferred = reactor.callLater(0, self.perform_service_download)
+
+    def stop(self):
+        """
+        Shutdown MIB Service download
+        """
+        self.log.debug('stopping')
+
+        self.cancel_deferred()
+        super(AdtnServiceDownloadTask, self).stop()
+
+    def check_status_and_state(self, results, operation=''):
+        """
+        Check the results of an OMCI response.  An exception is thrown
+        if the task was cancelled or an error was detected.
+
+        :param results: (OmciFrame) OMCI Response frame
+        :param operation: (str) what operation was being performed
+        :return: True if successful, False if the entity existed (already created)
+        """
+        omci_msg = results.fields['omci_message'].fields
+        status = omci_msg['success_code']
+        error_mask = omci_msg.get('parameter_error_attributes_mask', 'n/a')
+        failed_mask = omci_msg.get('failed_attributes_mask', 'n/a')
+        unsupported_mask = omci_msg.get('unsupported_attributes_mask', 'n/a')
+
+        self.log.debug(operation, status=status, error_mask=error_mask,
+                       failed_mask=failed_mask, unsupported_mask=unsupported_mask)
+
+        if status == RC.Success:
+            self.strobe_watchdog()
+            return True
+
+        elif status == RC.InstanceExists:
+            return False
+
+        raise ServiceDownloadFailure('{} failed with a status of {}, error_mask: {}, failed_mask: {}, unsupported_mask: {}'
+                                     .format(operation, status, error_mask, failed_mask, unsupported_mask))
+
+    @inlineCallbacks
+    def perform_service_download(self):
+        """
+        Send the commands to minimally configure the PON, Bridge, and
+        UNI ports for this device. The application of any service flows
+        and other characteristics are done once resources (gem-ports, tconts, ...)
+        have been defined.
+        """
+        self.log.info('perform-service-download')
+
+        device = self._handler.adapter_agent.get_device(self.device_id)
+
+        def resources_available():
+            # TODO: Rework for non-xpon mode
+            if self._handler.xpon_support:
+                return (device.vlan > 0 and
+                        len(self._handler.uni_ports) > 0 and
+                        len(self._handler.pon_port.tconts) and
+                        len(self._handler.pon_port.gem_ports))
+            else:
+                return (len(self._handler.uni_ports) > 0 and
+                        len(self._handler.pon_port.tconts) and
+                        len(self._handler.pon_port.gem_ports))
+
+        if self._handler.enabled and resources_available():
+            device.reason = 'Performing Service OMCI Download'
+            self._handler.adapter_agent.update_device(device)
+
+            try:
+                # Lock the UNI ports to prevent any alarms during initial configuration
+                # of the ONU
+                self.strobe_watchdog()
+                # Provision the initial bridge configuration
+                yield self.perform_service_specific_steps()
+
+                # And re-enable the UNIs if needed
+                yield self.enable_unis(self._handler.uni_ports, False)
+
+                # If here, we are done
+                device = self._handler.adapter_agent.get_device(self.device_id)
+
+                device.reason = ''
+                self._handler.adapter_agent.update_device(device)
+                self.deferred.callback('service-download-success')
+
+            except TimeoutError as e:
+                self.deferred.errback(failure.Failure(e))
+
+        else:
+            # TODO: Provide better error reason, what was missing...
+            e = ServiceResourcesFailure('Required resources are not available')
+            self.deferred.errback(failure.Failure(e))
+
+    @inlineCallbacks
+    def perform_service_specific_steps(self):
+        omci_cc = self._onu_device.omci_cc
+        frame = None
+
+        try:
+            ################################################################################
+            # TCONTS
+            #
+            #  EntityID will be referenced by:
+            #            - GemPortNetworkCtp
+            #  References:
+            #            - ONU created TCONT (created on ONU startup)
+
+            tcont_idents = self._onu_device.query_mib(Tcont.class_id)
+            self.log.debug('tcont-idents', tcont_idents=tcont_idents)
+
+            for tcont in self._handler.pon_port.tconts.itervalues():
+                free_entity_id = next((k for k, v in tcont_idents.items()
+                                       if isinstance(k, int) and
+                                       v.get('attributes', {}).get('alloc_id', 0) == 0xFFFF), None)
+                if free_entity_id is None:
+                    self.log.error('no-available-tconts')
+                    break
+                # TODO: Need to restore on failure.  Need to check status/results
+                yield tcont.add_to_hardware(omci_cc, free_entity_id)
+
+            ################################################################################
+            # GEMS  (GemPortNetworkCtp and GemInterworkingTp)
+            #
+            #  For both of these MEs, the entity_id is the GEM Port ID. The entity id of the
+            #  GemInterworkingTp ME could be different since it has an attribute to specify
+            #  the GemPortNetworkCtp entity id.
+            #
+            #  TODO: In the GEM Port routine to add, it has a hardcoded upstream TM ID of 0x8000
+            #        for the GemPortNetworkCtp ME
+            #
+            #  GemPortNetworkCtp
+            #    EntityID will be referenced by:
+            #              - GemInterworkingTp
+            #    References:
+            #              - TCONT
+            #              - Hardcoded upstream TM Entity ID
+            #              - (Possibly in Future) Upstream Traffic descriptor profile pointer
+            #
+            #  GemInterworkingTp
+            #    EntityID will be referenced by:
+            #              - Ieee8021pMapperServiceProfile
+            #    References:
+            #              - GemPortNetworkCtp
+            #              - Ieee8021pMapperServiceProfile
+            #              - GalEthernetProfile
+            #
+
+            for gem_port in self._handler.pon_port.gem_ports.itervalues():
+                tcont = gem_port.tcont
+                if tcont is None:
+                    self.log.error('unknown-tcont-reference', gem_id=gem_port.gem_id)
+                    continue
+                # TODO: Need to restore on failure.  Need to check status/results
+                yield gem_port.add_to_hardware(omci_cc,
+                                               tcont.entity_id,
+                                               self._ieee_mapper_service_profile_entity_id,
+                                               self._gal_enet_profile_entity_id)
+
+            ################################################################################
+            # Update the IEEE 802.1p Mapper Service Profile config
+            #
+            #  EntityID was created prior to this call. This is a set
+            #
+            #  References:
+            #            - Gem Interworking TPs are set here
+            #
+            # TODO: All p-bits currently go to the one and only GEMPORT ID for now
+            gem_ports = self._handler.pon_port.gem_ports
+            gem_entity_ids = [gem_port.entity_id for _, gem_port in gem_ports.items()] \
+                if len(gem_ports) else [OmciNullPointer]
+
+            frame = Ieee8021pMapperServiceProfileFrame(
+                self._ieee_mapper_service_profile_entity_id,  # 802.1p mapper Service Mapper Profile ID
+                interwork_tp_pointers=gem_entity_ids          # Interworking TP IDs
+            ).set()
+            results = yield omci_cc.send(frame)
+            self.check_status_and_state(results, 'set-8021p-mapper-service-profile')
+
+            ################################################################################
+            # Create Extended VLAN Tagging Operation config (PON-side)
+            #
+            #  EntityID relates to the VLAN TCIS
+            #  References:
+            #            - VLAN TCIS from previously created VLAN Tagging filter data
+            #            - PPTP Ethernet UNI
+            #
+            # TODO: add entry here for additional UNI interfaces
+
+            attributes = dict(
+                association_type=2,                                 # Assoc Type, PPTP Ethernet UNI
+                associated_me_pointer=self._ethernet_uni_entity_id  # Assoc ME, PPTP Entity Id
+            )
+
+            frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
+                self._vlan_config_entity_id,
+                attributes=attributes
+            ).create()
+            results = yield omci_cc.send(frame)
+            self.check_status_and_state(results, 'create-extended-vlan-tagging-operation-configuration-data')
+
+            ################################################################################
+            # Update Extended VLAN Tagging Operation Config Data
+            #
+            # Specifies the TPIDs in use and that operations in the downstream direction are
+            # inverse to the operations in the upstream direction
+            # TODO: Downstream mode may need to be modified once we work more on the flow rules
+
+            attributes = dict(
+                input_tpid=self._input_tpid,    # input TPID
+                output_tpid=self._output_tpid,  # output TPID
+                downstream_mode=0,              # inverse of upstream
+            )
+            frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
+                self._vlan_config_entity_id,
+                attributes=attributes
+            ).set()
+            results = yield omci_cc.send(frame)
+            self.check_status_and_state(results, 'set-extended-vlan-tagging-operation-configuration-data')
+
+            ################################################################################
+            # Update Extended VLAN Tagging Operation Config Data
+            #
+            # parameters: Entity Id ( 0x900), Filter Inner Vlan Id(0x1000-4096,do not filter on Inner vid,
+            #             Treatment Inner Vlan Id : 2
+
+            attributes = dict(
+                received_frame_vlan_tagging_operation_table=
+                VlanTaggingOperation(
+                    filter_outer_priority=15,  # This entry is not a double-tag rule
+                    filter_outer_vid=4096,     # Do not filter on the outer VID value
+                    filter_outer_tpid_de=0,    # Do not filter on the outer TPID field
+
+                    filter_inner_priority=15,  # This is a no-tag rule, ignore all other VLAN tag filter fields
+                    filter_inner_vid=0x1000,   # Do not filter on the inner VID
+                    filter_inner_tpid_de=0,    # Do not filter on inner TPID field
+
+                    filter_ether_type=0,         # Do not filter on EtherType
+                    treatment_tags_to_remove=0,  # Remove 0 tags
+
+                    treatment_outer_priority=15,  # Do not add an outer tag
+                    treatment_outer_vid=0,        # n/a
+                    treatment_outer_tpid_de=0,    # n/a
+
+                    treatment_inner_priority=0,      # Add an inner tag and insert this value as the priority
+                    treatment_inner_vid=self._cvid,  # use this value as the VID in the inner VLAN tag
+                    treatment_inner_tpid_de=4,       # set TPID
+                )
+            )
+            frame = ExtendedVlanTaggingOperationConfigurationDataFrame(
+                self._vlan_config_entity_id,  # Entity ID
+                attributes=attributes         # See above
+            ).set()
+            results = yield omci_cc.send(frame)
+            self.check_status_and_state(results, 'set-extended-vlan-tagging-operation-configuration-data-untagged')
+
+            ################################################################################
+            ################################################################################
+            ################################################################################
+            # BP: This is for AT&T RG's                #
+            #   TODO: CB: NOTE: TRY THIS ONCE OTHER SEQUENCES WORK
+            #
+            # Set AR - ExtendedVlanTaggingOperationConfigData
+            #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
+            # results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(
+            #                                 0x900,  # Entity ID
+            #                                 8,      # Filter Inner Priority, do not filter on Inner Priority
+            #                                 0,    # Filter Inner VID, this will be 0 in CORD
+            #                                 0,      # Filter Inner TPID DE
+            #                                 1,      # Treatment tags, number of tags to remove
+            #                                 8,      # Treatment inner priority, copy Inner Priority
+            #                                 2)   # Treatment inner VID, this will be 2 in CORD
+            # Set AR - ExtendedVlanTaggingOperationConfigData
+            #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
+            # results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(
+            #                                 0x200,  # Entity ID
+            #                                 8,      # Filter Inner Priority
+            #                                 0,      # Filter Inner VID
+            #                                 0,      # Filter Inner TPID DE
+            #                                 1,      # Treatment tags to remove
+            #                                 8,      # Treatment inner priority
+            #                                 cvid)   # Treatment inner VID
+            # Set AR - ExtendedVlanTaggingOperationConfigData
+            #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to untagged pkts - c-vid
+            # results = yield omci.send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(
+            #                                0x100,   # Entity ID            BP: Oldvalue 0x202
+            #                                0x1000,  # Filter Inner VID     BP: Oldvalue 0x1000
+            #                                cvid)    # Treatment inner VID  BP: cvid
+            # success = results.fields['omci_message'].fields['success_code'] == 0
+            # error_mask = results.fields['omci_message'].fields['parameter_error_attributes_mask']
+
+            ###############################################################################
+
+        except TimeoutError as e:
+            self.log.warn('rx-timeout-2', frame=frame)
+            raise
+
+        except Exception as e:
+            self.log.exception('omci-setup-2', e=e)
+            raise
+
+        returnValue(None)
+
+    @inlineCallbacks
+    def enable_unis(self, unis, force_lock):
+        """
+        Lock or unlock one or more UNI ports
+
+        :param unis: (list) of UNI objects
+        :param force_lock: (boolean) If True, force lock regardless of enabled state
+        """
+        omci_cc = self._onu_device.omci_cc
+        frame = None
+
+        for uni in unis:
+            ################################################################################
+            #  Lock/Unlock UNI  -  0 to Unlock, 1 to lock
+            #
+            #  EntityID is referenced by:
+            #            - MAC bridge port configuration data for the UNI side
+            #  References:
+            #            - Nothing
+            try:
+                state = 1 if force_lock or not uni.enabled else 0
+                frame = PptpEthernetUniFrame(uni.entity_id,
+                                             attributes=dict(administrative_state=state)).set()
+                results = yield omci_cc.send(frame)
+                self.check_status_and_state(results, 'set-pptp-ethernet-uni-lock-restore')
+
+            except TimeoutError:
+                self.log.warn('rx-timeout', frame=frame)
+                raise
+
+            except Exception as e:
+                self.log.exception('omci-failure', e=e)
+                raise
+
+        returnValue(None)
+
+
+
+
+
+
+
+
+
diff --git a/voltha/adapters/adtran_onu/omci/omci.py b/voltha/adapters/adtran_onu/omci/omci.py
index 2b008a5..9e1ba37 100644
--- a/voltha/adapters/adtran_onu/omci/omci.py
+++ b/voltha/adapters/adtran_onu/omci/omci.py
@@ -52,6 +52,8 @@
         self._connectivity_subscription = None
         self._capabilities_subscription = None
 
+        self._service_downloaded = False
+        self._mib_downloaded = False
         self._mib_download_task = None
         self._mib_download_deferred = None
 
@@ -94,6 +96,7 @@
         self._unsubscribe_to_events()
         self._onu_omci_device.stop()        # Will also cancel any running tasks/state-machines
 
+        self._mib_downloaded = False
         self._mib_download_task = None
         self._bridge_initialized = False
         self._in_sync_reached = False
@@ -198,11 +201,15 @@
             # Save entity_id of PON ports
             self._handler.pon_ports[0].entity_id = ani_g.keys()[0]
 
-            # Save entity_id for UNI ports
-            uni_entity_ids = uni_g.keys()
-            uni_entity_ids.sort()
-            for uni in self._handler.uni_ports:
-                uni.entity_id = uni_entity_ids.pop(0)
+            # Save entity_id for UNI ports (this is only for xPON mode code).  For the
+            # non-xPON mode, we save the entity IDs during the mib-in-sync handler when
+            # we create the UNI ports.
+
+            if self._handler.xpon_support:
+                uni_entity_ids = uni_g.keys()
+                uni_entity_ids.sort()
+                for uni in self._handler.uni_ports:
+                    uni.entity_id = uni_entity_ids.pop(0)
 
             self._total_tcont_count = ani_g.get('total-tcont-count')
             self._qos_flexibility = config.qos_configuration_flexibility or 0
@@ -321,8 +328,16 @@
         """
         if self._capabilities_subscription is not None:
             from adtn_mib_download_task import AdtnMibDownloadTask
+            from adtn_service_download_task import AdtnServiceDownloadTask
 
             def success(_results):
+                if self._mib_downloaded:
+                    self._service_downloaded = True
+                else:
+                    # Now try the services
+                    self._mib_downloaded = True
+                    reactor.callLater(0, self.capabilities_handler)
+
                 self._mib_download_task = None
 
             def failure(_reason):
@@ -330,7 +345,11 @@
                 # TODO: Handle failure, retry for now?
                 self._mib_download_deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
                                                                 self.capabilities_handler)
-            self._mib_download_task = AdtnMibDownloadTask(self.omci_agent, self._handler)
+            if not self._mib_downloaded:
+                self._mib_download_task = AdtnMibDownloadTask(self.omci_agent, self._handler)
+            else:
+                self._mib_download_task = AdtnServiceDownloadTask(self.omci_agent, self._handler)
+
             self._mib_download_deferred = self._onu_omci_device.task_runner.queue_task(self._mib_download_task)
             self._mib_download_deferred.addCallbacks(success, failure)
 
diff --git a/voltha/adapters/adtran_onu/uni_port.py b/voltha/adapters/adtran_onu/uni_port.py
index 32636a2..0c53b2b 100644
--- a/voltha/adapters/adtran_onu/uni_port.py
+++ b/voltha/adapters/adtran_onu/uni_port.py
@@ -24,7 +24,7 @@
 
 class UniPort(object):
     """Wraps southbound-port(s) support for ONU"""
-    DEFAULT_UNTAGGED_VLAN = 4092
+    DEFAULT_UNTAGGED_VLAN = 4091        # TODO: BroadCom Default.  Need a better way to define this
 
     def __init__(self, handler, name, port_no, ofp_port_no, subscriber_vlan=None,
                  untagged_vlan=None):
@@ -40,6 +40,7 @@
         self._subscriber_vlan = subscriber_vlan
         self._untagged_vlan = untagged_vlan
         self._entity_id = None                  # TODO: Use port number from UNI-G entity ID
+        self._mac_bridge_port_num = 0
 
         self._admin_state = AdminState.ENABLED
         self._oper_status = OperStatus.ACTIVE
@@ -110,6 +111,20 @@
         return self._port_number
 
     @property
+    def mac_bridge_port_num(self):
+        """
+        Port number used when creating MacBridgePortConfigurationDataFrame port number
+        :return: (int) port number
+        """
+        self.log.debug('function-entry')
+        return self._mac_bridge_port_num
+
+    @mac_bridge_port_num.setter
+    def mac_bridge_port_num(self, value):
+        self.log.debug('function-entry')
+        self._mac_bridge_port_num = value
+
+    @property
     def entity_id(self):
         """
         OMCI UNI_G entity ID for port
@@ -196,7 +211,7 @@
         """
         if self._port is None:
             self._port = Port(port_no=self.port_number,
-                              label='Ethernet port',
+                              label='vEth-{}'.format(self.port_number),
                               type=Port.ETHERNET_UNI,
                               admin_state=self._admin_state,
                               oper_status=self._oper_status)