ADTRAN ONU: Tech profile support

Change-Id: I49e3dc1c386d9ad8d9f2784b04bd375cd93fd511
diff --git a/voltha/adapters/adtran_onu/adtran_onu.py b/voltha/adapters/adtran_onu/adtran_onu.py
index d8ae1b9..dde4334 100755
--- a/voltha/adapters/adtran_onu/adtran_onu.py
+++ b/voltha/adapters/adtran_onu/adtran_onu.py
@@ -42,7 +42,7 @@
                                                device_handler_class=AdtranOnuHandler,
                                                name='adtran_onu',
                                                vendor='ADTRAN, Inc.',
-                                               version='1.24',
+                                               version='1.25',
                                                device_type='adtran_onu',
                                                vendor_id='ADTN',
                                                accepts_add_remove_flow_updates=False),  # TODO: Support flow-mods
diff --git a/voltha/adapters/adtran_onu/adtran_onu_handler.py b/voltha/adapters/adtran_onu/adtran_onu_handler.py
index f9ce52f..ccc0f7d 100644
--- a/voltha/adapters/adtran_onu/adtran_onu_handler.py
+++ b/voltha/adapters/adtran_onu/adtran_onu_handler.py
@@ -33,7 +33,6 @@
 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 *
 from common.tech_profile.tech_profile import TechProfile
 from voltha.core.config.config_backend import ConsulStore
@@ -66,7 +65,6 @@
         self._enabled = False
         self.pm_metrics = None
         self.alarms = None
-        self._mgmt_gemport_aes = False
 
         self._openomci = OMCI(self, adapter.omci_agent)
         self._in_sync_subscription = None
@@ -81,18 +79,16 @@
         # Flow entries
         self._flows = dict()
 
-        # OMCI resources
-        # TODO: Some of these could be dynamically chosen
+        # OMCI resources               # TODO: Some of these could be dynamically chosen
         self.vlan_tcis_1 = 0x900
         self.mac_bridge_service_profile_entity_id = self.vlan_tcis_1
-        self.gal_enet_profile_entity_id = 0     # Was 0x100, but ONU seems to overwrite and use zero
+        self.gal_enet_profile_entity_id = 0
 
         # Technology profile related values
         self.incoming_messages = DeferredQueue()
         self.event_messages = DeferredQueue()
         self._tp_service_specific_task = dict()
         self._tech_profile_download_done = dict()
-        self._upstream_channel_speed = 0                # TODO: Deprecate
 
         # Initialize KV store client
         self.args = registry('main').get_args()
@@ -137,26 +133,6 @@
                 self.stop()
 
     @property
-    def mgmt_gemport_aes(self):
-        return self._mgmt_gemport_aes
-
-    @mgmt_gemport_aes.setter
-    def mgmt_gemport_aes(self, value):
-        if self._mgmt_gemport_aes != value:
-            self._mgmt_gemport_aes = value
-            # TODO: Anything else
-
-    @property
-    def upstream_channel_speed(self):
-        return self._upstream_channel_speed
-
-    @upstream_channel_speed.setter
-    def upstream_channel_speed(self, value):
-        if self._upstream_channel_speed != value:
-            self._upstream_channel_speed = value
-            # TODO: Anything else
-
-    @property
     def openomci(self):
         return self._openomci
 
@@ -438,9 +414,8 @@
                         'min-threshold': gem['discard_config']['min_threshold'],
                     },
                 }
-                gem_port = OnuGemPort.create(self, gem_data,
-                                             tcont.alloc_id,
-                                             tech_profile_id,
+                gem_port = OnuGemPort.create(self, gem_data, tcont.alloc_id,
+                                             tech_profile_id, uni_id,
                                              self._pon.next_gem_entity_id)
                 self._pon.add_gem_port(gem_port)
 
@@ -481,35 +456,40 @@
                 def success(_results):
                     self.log.info("tech-profile-config-done-successfully")
                     device = self.adapter_agent.get_device(self.device_id)
-                    device.reason = 'tech-profile-config-download-success'
+                    device.reason = 'Tech Profile config Success'
                     self.adapter_agent.update_device(device)
+
                     if tp_path in self._tp_service_specific_task[uni_id]:
                         del self._tp_service_specific_task[uni_id][tp_path]
+
                     self._tech_profile_download_done[uni_id][tp_path] = True
 
                 def failure(_reason):
                     self.log.warn('tech-profile-config-failure-retrying', reason=_reason)
                     device = self.adapter_agent.get_device(self.device_id)
-                    device.reason = 'tech-profile-config-download-failure-retrying'
+                    device.reason = 'Tech Profile config failed-retrying'
                     self.adapter_agent.update_device(device)
+
                     if tp_path in self._tp_service_specific_task[uni_id]:
                         del self._tp_service_specific_task[uni_id][tp_path]
-                    self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT, self.load_and_configure_tech_profile,
+
+                    self._deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
+                                                       self.load_and_configure_tech_profile,
                                                        uni_id, tp_path)
 
                 self.log.info('downloading-tech-profile-configuration')
                 tp_task = AdtnTpServiceSpecificTask(self.openomci.omci_agent, self, uni_id)
 
                 self._tp_service_specific_task[uni_id][tp_path] = tp_task
-                # self._deferred = self.openomci.onu_omci_device.task_runner.queue_task(tp_task)
-                # self._deferred.addCallbacks(success, failure)
+                self._deferred = self.openomci.onu_omci_device.task_runner.queue_task(tp_task)
+                self._deferred.addCallbacks(success, failure)
 
             except Exception as e:
                 self.log.exception("error-loading-tech-profile", e=e)
         else:
             self.log.info("tech-profile-config-already-done")
 
-    def update_pm_config(self, device, pm_config):
+    def update_pm_config(self, _device, pm_config):
         # TODO: This has not been tested
         self.log.info('update_pm_config', pm_config=pm_config)
         self.pm_metrics.update(pm_config)
@@ -644,8 +624,8 @@
         device.reason = ''
         self.adapter_agent.update_device(device)
 
-        # if reregister:
-        #     self.adapter_agent.register_for_inter_adapter_messages()
+        if reregister:
+            self.adapter_agent.register_for_inter_adapter_messages()
 
         self.log.info('reboot-complete', device_id=self.device_id)
 
@@ -663,8 +643,7 @@
     def disable(self):
         self.log.info('disabling', device_id=self.device_id)
         try:
-        # Get the latest device reference
-
+            # Get the latest device reference
             device = self.adapter_agent.get_device(self.device_id)
 
             # Disable all ports on that device
@@ -706,7 +685,7 @@
 
         # And disable OMCI as well
         self.enabled = False
-        self.log.info('disabled', device_id=device.id)
+        self.log.info('disabled')
 
     def reenable(self):
         self.log.info('re-enabling', device_id=self.device_id)
@@ -756,10 +735,10 @@
             self.enabled = True
             self.adapter_agent.update_device(device)
 
-            self.log.info('re-enabled', device_id=device.id)
+            self.log.info('re-enabled')
 
         except Exception, e:
-            self.log.exception('error-reenabling', e=e)
+            self.log.exception('error-re-enabling', e=e)
 
     def delete(self):
         self.log.info('deleting', device_id=self.device_id)
diff --git a/voltha/adapters/adtran_onu/omci/adtn_tp_service_specific_task.py b/voltha/adapters/adtran_onu/omci/adtn_tp_service_specific_task.py
index 7dd375a..2d7ea69 100644
--- a/voltha/adapters/adtran_onu/omci/adtn_tp_service_specific_task.py
+++ b/voltha/adapters/adtran_onu/omci/adtn_tp_service_specific_task.py
@@ -14,15 +14,15 @@
 # limitations under the License.
 
 import structlog
-from common.frameio.frameio import hexify
 from twisted.internet import reactor
-from twisted.internet.defer import inlineCallbacks, returnValue, TimeoutError, failure
+from twisted.internet.defer import inlineCallbacks, TimeoutError, failure, returnValue
 from voltha.extensions.omci.omci_me import *
 from voltha.extensions.omci.tasks.task import Task
 from voltha.extensions.omci.omci_defs import *
-from voltha.adapters.brcm_openomci_onu.uni_port import *
-from voltha.adapters.brcm_openomci_onu.pon_port \
-    import BRDCM_DEFAULT_VLAN, TASK_PRIORITY, DEFAULT_TPID, DEFAULT_GEM_PAYLOAD
+from voltha.adapters.adtran_onu.omci.omci import OMCI
+from voltha.adapters.adtran_onu.uni_port import *
+from voltha.adapters.adtran_onu.onu_tcont import OnuTCont
+from voltha.adapters.adtran_onu.onu_gem_port import OnuGemPort
 
 OP = EntityOperations
 RC = ReasonCodes
@@ -45,6 +45,9 @@
     Adtran OpenOMCI Tech-Profile Download Task
     """
     name = "Adtran Tech-Profile Download Task"
+    task_priority = Task.DEFAULT_PRIORITY + 10
+    default_tpid = 0x8100                       # TODO: Move to a better location
+    default_gem_payload = 48
 
     def __init__(self, omci_agent, handler, uni_id):
         """
@@ -53,15 +56,13 @@
         :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
         :param device_id: (str) ONU Device ID
         """
-        log = structlog.get_logger(device_id=handler.device_id, uni_id=uni_id)
-        log.debug('function-entry')
+        self.log = structlog.get_logger(device_id=handler.device_id, uni_id=uni_id)
 
         super(AdtnTpServiceSpecificTask, self).__init__(AdtnTpServiceSpecificTask.name,
                                                         omci_agent,
                                                         handler.device_id,
-                                                        priority=TASK_PRIORITY,
+                                                        priority=AdtnTpServiceSpecificTask.task_priority,
                                                         exclusive=False)
-        self.log = log
 
         self._onu_device = omci_agent.get_device(handler.device_id)
         self._local_deferred = None
@@ -70,21 +71,20 @@
         self._uni_port = handler.uni_ports[uni_id]
         assert self._uni_port.uni_id == uni_id
 
-        # Port numbers
-        self._input_tpid = DEFAULT_TPID
-        self._output_tpid = DEFAULT_TPID
+        self._input_tpid = AdtnTpServiceSpecificTask.default_tpid
+        self._output_tpid = AdtnTpServiceSpecificTask.default_tpid
 
-        self._vlan_tcis_1 = BRDCM_DEFAULT_VLAN
-        self._cvid = BRDCM_DEFAULT_VLAN
+        self._vlan_tcis_1 = OMCI.DEFAULT_UNTAGGED_VLAN
+        self._cvid = OMCI.DEFAULT_UNTAGGED_VLAN
         self._vlan_config_entity_id = self._vlan_tcis_1
-        self._max_gem_payload = DEFAULT_GEM_PAYLOAD
+        self._max_gem_payload = AdtnTpServiceSpecificTask.default_gem_payload
 
         # Entity IDs. IDs with values can probably be most anything for most ONUs,
         #             IDs set to None are discovered/set
 
         self._mac_bridge_service_profile_entity_id = handler.mac_bridge_service_profile_entity_id
-        self._ieee_mapper_service_profile_entity_id = pon_port.ieee_mapper_service_profile_entity_id
-        self._mac_bridge_port_ani_entity_id = pon_port.mac_bridge_port_ani_entity_id
+        self._ieee_mapper_service_profile_entity_id = pon_port.hsi_8021p_mapper_entity_id
+        self._mac_bridge_port_ani_entity_id = pon_port.hsi_mac_bridge_port_ani_entity_id
         self._gal_enet_profile_entity_id = handler.gal_enet_profile_entity_id
 
         # Extract the current set of TCONT and GEM Ports from the Handler's pon_port that are
@@ -154,7 +154,7 @@
             return True
 
         elif status == RC.InstanceExists:
-            return False
+            return False           # For Creates issued during task retries
 
         raise TechProfileDownloadFailure(
             '{} failed with a status of {}, error_mask: {}, failed_mask: {}, unsupported_mask: {}'
@@ -162,9 +162,25 @@
 
     @inlineCallbacks
     def perform_service_specific_steps(self):
-        self.log.debug('function-entry')
+        """
+        Install the Technology Profile specific ME instances into the ONU. The
+        initial bridge setup was performed after the capabilities were discovered.
+
+        This task is called near the end of the ONU Tech profile setup when the
+        ONU receives technology profile info from the OLT over the inter-adapter channel
+        """
+        self.log.debug('setting-up-tech-profile-me-instances')
+
+        if len(self._tconts) == 0:
+            self.deferred.errback(failure.Failure(TechProfileResourcesFailure('No TCONTs assigned')))
+            returnValue('no-resources')
+
+        if len(self._gem_ports) == 0:
+            self.deferred.errback(failure.Failure(TechProfileResourcesFailure('No GEM Ports assigned')))
+            returnValue('no-resources')
 
         omci_cc = self._onu_device.omci_cc
+        self.strobe_watchdog()
 
         try:
             ################################################################################
@@ -173,31 +189,35 @@
             #  EntityID will be referenced by:
             #            - GemPortNetworkCtp
             #  References:
-            #            - ONU created TCONT (created on ONU startup)
+            #            - ONU created TCONT (created on ONU tech profile startup)
 
             tcont_idents = self._onu_device.query_mib(Tcont.class_id)
             self.log.debug('tcont-idents', tcont_idents=tcont_idents)
 
             for tcont in self._tconts:
-                free_entity_id = None
-                for k, v in tcont_idents.items():
-                    alloc_check = v.get('attributes', {}).get('alloc_id', 0)
-                    # Some onu report both to indicate an available tcont
-                    if alloc_check == 0xFF or alloc_check == 0xFFFF:
-                        free_entity_id = k
-                        break
-                    else:
-                        free_entity_id = None
+                if tcont.entity_id is not None:
+                    continue             # Already installed
 
-                self.log.debug('tcont-loop', free_entity_id=free_entity_id, alloc_id=tcont.alloc_id)
+                free_alloc_ids = {OnuTCont.FREE_TCONT_ALLOC_ID,
+                                  OnuTCont.FREE_GPON_TCONT_ALLOC_ID}
+
+                free_entity_id = next((k for k, v in tcont_idents.items()
+                                       if isinstance(k, int) and
+                                       v.get('attributes', {}).get('alloc_id', 0) in
+                                       free_alloc_ids), None)
 
                 if free_entity_id is None:
                     self.log.error('no-available-tconts')
-                    break
+                    raise TechProfileResourcesFailure('No Available TConts')
 
-                # TODO: Need to restore on failure.  Need to check status/results
-                results = yield tcont.add_to_hardware(omci_cc, free_entity_id)
-                self.check_status_and_state(results, 'create-tcont')
+                try:
+                    prev_alloc_id = tcont_idents[free_entity_id].get('attributes').get('alloc_id')
+                    results = yield tcont.add_to_hardware(omci_cc, free_entity_id, prev_alloc_id=prev_alloc_id)
+                    self.check_status_and_state(results, 'create-tcont')
+
+                except Exception as e:
+                    self.log.exception('tcont-set', e=e, eid=free_entity_id)
+                    raise
 
             ################################################################################
             # GEMS  (GemPortNetworkCtp and GemInterworkingTp)
@@ -224,21 +244,23 @@
             #              - Ieee8021pMapperServiceProfile
             #              - GalEthernetProfile
             #
-
             onu_g = self._onu_device.query_mib(OntG.class_id)
+
             # If the traffic management option attribute in the ONU-G ME is 0
             # (priority controlled) or 2 (priority and rate controlled), this
             # pointer specifies the priority queue ME serving this GEM port
             # network CTP. If the traffic management option attribute is 1
             # (rate controlled), this attribute redundantly points to the
             # T-CONT serving this GEM port network CTP.
-            traffic_mgmt_opt = \
-                onu_g.get('attributes', {}).get('traffic_management_options', 0)
+
+            traffic_mgmt_opt = onu_g.get('attributes', {}).get('traffic_management_options', 0)
             self.log.debug("traffic-mgmt-option", traffic_mgmt_opt=traffic_mgmt_opt)
 
             prior_q = self._onu_device.query_mib(PriorityQueueG.class_id)
+
             for k, v in prior_q.items():
                 self.log.debug("prior-q", k=k, v=v)
+                self.strobe_watchdog()
 
                 try:
                     _ = iter(v)
@@ -249,29 +271,34 @@
                     related_port = v['attributes']['related_port']
                     if v['instance_id'] & 0b1000000000000000:
                         tcont_me = (related_port & 0xffff0000) >> 16
+
                         if tcont_me not in self.tcont_me_to_queue_map:
                             self.log.debug("prior-q-related-port-and-tcont-me",
-                                            related_port=related_port,
-                                            tcont_me=tcont_me)
+                                           related_port=related_port,
+                                           tcont_me=tcont_me)
                             self.tcont_me_to_queue_map[tcont_me] = list()
 
                         self.tcont_me_to_queue_map[tcont_me].append(k)
                     else:
                         uni_port = (related_port & 0xffff0000) >> 16
-                        if uni_port ==  self._uni_port.entity_id:
+
+                        if uni_port == self._uni_port.entity_id:
                             if uni_port not in self.uni_port_to_queue_map:
                                 self.log.debug("prior-q-related-port-and-uni-port-me",
-                                                related_port=related_port,
-                                                uni_port_me=uni_port)
+                                               related_port=related_port,
+                                               uni_port_me=uni_port)
                                 self.uni_port_to_queue_map[uni_port] = list()
 
                             self.uni_port_to_queue_map[uni_port].append(k)
 
-
             self.log.debug("ul-prior-q", ul_prior_q=self.tcont_me_to_queue_map)
             self.log.debug("dl-prior-q", dl_prior_q=self.uni_port_to_queue_map)
 
             for gem_port in self._gem_ports:
+                self.strobe_watchdog()
+                if gem_port.entity_id is not None:
+                    continue                        # Already installed
+
                 # TODO: Traffic descriptor will be available after meter bands are available
                 tcont = gem_port.tcont
                 if tcont is None:
@@ -280,13 +307,14 @@
 
                 ul_prior_q_entity_id = None
                 dl_prior_q_entity_id = None
-                if gem_port.direction == "upstream" or \
-                        gem_port.direction == "bi-directional":
+
+                if gem_port.direction in {OnuGemPort.UPSTREAM, OnuGemPort.BIDIRECTIONAL}:
 
                     # Sort the priority queue list in order of priority.
                     # 0 is highest priority and 0x0fff is lowest.
                     self.tcont_me_to_queue_map[tcont.entity_id].sort()
                     self.uni_port_to_queue_map[self._uni_port.entity_id].sort()
+
                     # Get the priority queue associated with p-bit that is
                     # mapped to the gem port.
                     # p-bit-7 is highest priority and p-bit-0 is lowest
@@ -298,24 +326,22 @@
                     # of priority
                     for i, p in enumerate(gem_port.pbit_map):
                         if p == '1':
-                            ul_prior_q_entity_id = \
-                                self.tcont_me_to_queue_map[tcont.entity_id][i]
-                            dl_prior_q_entity_id = \
-                                self.uni_port_to_queue_map[self._uni_port.entity_id][i]
+                            ul_prior_q_entity_id = self.tcont_me_to_queue_map[tcont.entity_id][i]
+                            dl_prior_q_entity_id = self.uni_port_to_queue_map[self._uni_port.entity_id][i]
                             break
 
-                    assert ul_prior_q_entity_id is not None and \
-                           dl_prior_q_entity_id is not None
+                    assert ul_prior_q_entity_id is not None and dl_prior_q_entity_id is not None
 
                     # TODO: Need to restore on failure.  Need to check status/results
                     results = yield gem_port.add_to_hardware(omci_cc,
-                                                   tcont.entity_id,
-                                                   self._ieee_mapper_service_profile_entity_id +
+                                                             tcont.entity_id,
+                                                             self._ieee_mapper_service_profile_entity_id +
                                                              self._uni_port.mac_bridge_port_num,
-                                                   self._gal_enet_profile_entity_id,
-                                                   ul_prior_q_entity_id, dl_prior_q_entity_id)
+                                                             self._gal_enet_profile_entity_id,
+                                                             ul_prior_q_entity_id, dl_prior_q_entity_id)
                     self.check_status_and_state(results, 'create-gem-port')
-                elif gem_port.direction == "downstream":
+
+                elif gem_port.direction == OnuGemPort.DOWNSTREAM:
                     # Downstream is inverse of upstream
                     # TODO: could also be a case of multicast. Not supported for now
                     pass
@@ -328,17 +354,18 @@
             #  References:
             #            - Gem Interwork TPs are set here
             #
-
             gem_entity_ids = [OmciNullPointer] * 8
+
             for gem_port in self._gem_ports:
+                self.strobe_watchdog()
                 self.log.debug("tp-gem-port", entity_id=gem_port.entity_id, uni_id=gem_port.uni_id)
 
-                if gem_port.direction == "upstream" or \
-                        gem_port.direction == "bi-directional":
+                if gem_port.direction in {OnuGemPort.UPSTREAM, OnuGemPort.BIDIRECTIONAL}:
                     for i, p in enumerate(gem_port.pbit_map):
                         if p == '1':
                             gem_entity_ids[i] = gem_port.entity_id
-                elif gem_port.direction == "downstream":
+
+                elif gem_port.direction == OnuGemPort.DOWNSTREAM:
                     # Downstream gem port p-bit mapper is inverse of upstream
                     # TODO: Could also be a case of multicast. Not supported for now
                     pass
@@ -360,35 +387,31 @@
             #            - VLAN TCIS from previously created VLAN Tagging filter data
             #            - PPTP Ethernet or VEIP UNI
             #
-
             # TODO: do this for all uni/ports...
             # TODO: magic.  static variable for assoc_type
-
             # default to PPTP
-            if self._uni_port.type is UniType.VEIP:
-                association_type = 10
-            elif self._uni_port.type is UniType.PPTP:
-                association_type = 2
-            else:
-                association_type = 2
+            # if self._uni_port.type is UniType.VEIP:
+            #     association_type = 10
+            # elif self._uni_port.type is UniType.PPTP:
+            #     association_type = 2
+            # else:
+            association_type = 2
 
             attributes = dict(
                 association_type=association_type,                  # Assoc Type, PPTP/VEIP Ethernet UNI
-                associated_me_pointer=self._uni_port.entity_id,      # Assoc ME, PPTP/VEIP Entity Id
+                associated_me_pointer=self._uni_port.entity_id,     # Assoc ME, PPTP/VEIP Entity Id
 
                 # See VOL-1311 - Need to set table during create to avoid exception
                 # trying to read back table during post-create-read-missing-attributes
                 # But, because this is a R/W attribute. Some ONU may not accept the
                 # value during create. It is repeated again in a set below.
-                input_tpid=self._input_tpid,  # input TPID
+                input_tpid=self._input_tpid,    # input TPID
                 output_tpid=self._output_tpid,  # output TPID
             )
-
             msg = ExtendedVlanTaggingOperationConfigurationDataFrame(
                 self._mac_bridge_service_profile_entity_id + self._uni_port.mac_bridge_port_num,  # Bridge Entity ID
                 attributes=attributes
             )
-
             frame = msg.create()
             self.log.debug('openomci-msg', omci_msg=msg)
             results = yield omci_cc.send(frame)
@@ -401,12 +424,10 @@
                 output_tpid=self._output_tpid,  # output TPID
                 downstream_mode=0,              # inverse of upstream
             )
-
             msg = ExtendedVlanTaggingOperationConfigurationDataFrame(
                 self._mac_bridge_service_profile_entity_id + self._uni_port.mac_bridge_port_num,  # Bridge Entity ID
                 attributes=attributes
             )
-
             frame = msg.set()
             self.log.debug('openomci-msg', omci_msg=msg)
             results = yield omci_cc.send(frame)
@@ -420,34 +441,32 @@
                 # filter for untagged
                 # probably for eapol
                 # TODO: lots of magic
-                # TODO: magic 0x1000 / 4096?
                 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,
-                    filter_inner_vid=4096,
-                    filter_inner_tpid_de=0,
-                    filter_ether_type=0,
+                    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
 
-                    treatment_tags_to_remove=0,
-                    treatment_outer_priority=15,
-                    treatment_outer_vid=0,
-                    treatment_outer_tpid_de=0,
+                    filter_ether_type=0,         # Do not filter on EtherType
+                    treatment_tags_to_remove=0,  # Remove 0 tags
 
-                    treatment_inner_priority=0,
-                    treatment_inner_vid=self._cvid,
-                    treatment_inner_tpid_de=4,
+                    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
                 )
             )
-
             msg = ExtendedVlanTaggingOperationConfigurationDataFrame(
                 self._mac_bridge_service_profile_entity_id + self._uni_port.mac_bridge_port_num,  # Bridge Entity ID
                 attributes=attributes
             )
-
             frame = msg.set()
             self.log.debug('openomci-msg', omci_msg=msg)
             results = yield omci_cc.send(frame)
diff --git a/voltha/adapters/adtran_onu/omci/omci.py b/voltha/adapters/adtran_onu/omci/omci.py
index a9d44c0..67f6519 100644
--- a/voltha/adapters/adtran_onu/omci/omci.py
+++ b/voltha/adapters/adtran_onu/omci/omci.py
@@ -51,7 +51,7 @@
         self._connectivity_subscription = None
         self._capabilities_subscription = None
 
-        self._service_downloaded = False
+        # self._service_downloaded = False
         self._mib_downloaded = False
         self._mib_download_task = None
         self._mib_download_deferred = None
@@ -310,28 +310,20 @@
         """
         if self._capabilities_subscription is not None:
             from adtn_mib_download_task import AdtnMibDownloadTask
-            from adtn_service_download_task import AdtnServiceDownloadTask
             self._mib_download_task = None
 
             def success(_results):
-                device = self._handler.adapter_agent.get_device(self._handler.device_id)
-                device.reason = ''
-                self._handler.adapter_agent.update_device(device)
-
-                if self._mib_downloaded:
-                    self._service_downloaded = True
-                else:
-                    # Now try the services (HSI, ...) specific download
-                    self._mib_downloaded = True
-                    reactor.callLater(0, self.capabilities_handler, None, None)
-
+                dev = self._handler.adapter_agent.get_device(self._handler.device_id)
+                dev.reason = ''
+                self._handler.adapter_agent.update_device(dev)
+                self._mib_downloaded = True
                 self._mib_download_task = None
 
             def failure(reason):
                 self.log.error('mib-download-failure', reason=reason)
                 self._mib_download_task = None
-                device = self._handler.adapter_agent.get_device(self._handler.device_id)
-                self._handler.adapter_agent.update_device(device)
+                dev = self._handler.adapter_agent.get_device(self._handler.device_id)
+                self._handler.adapter_agent.update_device(dev)
                 self._mib_download_deferred = reactor.callLater(_STARTUP_RETRY_WAIT,
                                                                 self.capabilities_handler,
                                                                 None, None)
@@ -341,12 +333,15 @@
                 self._handler.adapter_agent.update_device(device)
                 self._mib_download_task = AdtnMibDownloadTask(self.omci_agent,
                                                               self._handler)
-            elif not self._service_downloaded:
-                device = self._handler.adapter_agent.get_device(self._handler.device_id)
-                device.reason = 'Initial Service Download'
-                self._handler.adapter_agent.update_device(device)
-                self._mib_download_task = AdtnServiceDownloadTask(self.omci_agent,
-                                                                  self._handler)
+
+            # TODO: Remove later.  Service specific ME download is now done as part of the
+            #                      Technology Profile setup
+            # elif not self._service_downloaded:
+            #     device = self._handler.adapter_agent.get_device(self._handler.device_id)
+            #     device.reason = 'Initial Service Download'
+            #     self._handler.adapter_agent.update_device(device)
+            #     self._mib_download_task = AdtnServiceDownloadTask(self.omci_agent,
+            #                                                       self._handler)
             if self._mib_download_task is not None:
                 self._mib_download_deferred = \
                     self._onu_omci_device.task_runner.queue_task(self._mib_download_task)
diff --git a/voltha/adapters/adtran_onu/onu_gem_port.py b/voltha/adapters/adtran_onu/onu_gem_port.py
index cb5ae45..89065ab 100644
--- a/voltha/adapters/adtran_onu/onu_gem_port.py
+++ b/voltha/adapters/adtran_onu/onu_gem_port.py
@@ -1,4 +1,4 @@
-
+#
 # Copyright 2017-present Adtran, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,16 +24,18 @@
     """
     Adtran ONU specific implementation
     """
-    def __init__(self, gem_data, alloc_id, tech_profile_id, entity_id,
-                 multicast=False,
-                 traffic_class=None,
-                 handler=None,
-                 is_mock=False):
+    UPSTREAM = 1
+    DOWNSTREAM = 2
+    BIDIRECTIONAL = 3
+
+    def __init__(self, handler, gem_data, alloc_id, tech_profile_id,
+                 uni_id, entity_id,
+                 multicast=False, traffic_class=None, is_mock=False):
         gem_id = gem_data['gemport-id']
         encryption = gem_data['encryption']
-        super(OnuGemPort, self).__init__(gem_id, alloc_id, tech_profile_id,
+        super(OnuGemPort, self).__init__(gem_id, alloc_id, uni_id,
+                                         tech_profile_id,
                                          encryption=encryption,
-                                         omci_transport=False,
                                          multicast=multicast,
                                          traffic_class=traffic_class,
                                          handler=handler,
@@ -54,15 +56,16 @@
         return self._tcont_entity_id is not None and self._interworking
 
     @staticmethod
-    def create(handler, gem_data, alloc_id, tech_profile_id, entity_id):
+    def create(handler, gem_data, alloc_id, tech_profile_id, uni_id, entity_id):
         # TODO: Only a minimal amount of info from the 'gem_port' dictionary
         #       is currently used to create the GEM ports.
+        return OnuGemPort(handler, gem_data, alloc_id,
+                          tech_profile_id, uni_id, entity_id)
 
-        return OnuGemPort(gem_data,
-                          alloc_id,
-                          tech_profile_id,
-                          entity_id,
-                          handler=handler)
+    @property
+    def tcont(self):
+        """ Get the associated TCONT object """
+        return self._handler.pon_port.tconts.get(self.alloc_id)
 
     @inlineCallbacks
     def add_to_hardware(self, omci,
diff --git a/voltha/adapters/adtran_onu/onu_tcont.py b/voltha/adapters/adtran_onu/onu_tcont.py
index 3031ca8..9751986 100644
--- a/voltha/adapters/adtran_onu/onu_tcont.py
+++ b/voltha/adapters/adtran_onu/onu_tcont.py
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 import structlog
-from twisted.internet.defer import  inlineCallbacks, returnValue, succeed
+from twisted.internet.defer import  inlineCallbacks, returnValue
 
 from voltha.adapters.adtran_olt.xpon.tcont import TCont
 from voltha.adapters.adtran_olt.xpon.traffic_descriptor import TrafficDescriptor
@@ -25,18 +25,17 @@
     """
     Adtran ONU specific implementation
     """
-    free_tcont_alloc_id = 0xFFFF
-    free_gpon_tcont_alloc_id = 0xFF     # SFU may use this to indicate a free TCONT
+    FREE_TCONT_ALLOC_ID = 0xFFFF
+    FREE_GPON_TCONT_ALLOC_ID = 0xFF     # SFU may use this to indicate a free TCONT
 
     def __init__(self, handler, alloc_id, sched_policy, tech_profile_id, uni_id, traffic_descriptor, is_mock=False):
-        super(OnuTCont, self).__init__(alloc_id, tech_profile_id, traffic_descriptor, is_mock=is_mock)
+        super(OnuTCont, self).__init__(alloc_id, tech_profile_id, traffic_descriptor, uni_id, is_mock=is_mock)
         self.log = structlog.get_logger(device_id=handler.device_id, alloc_id=alloc_id)
 
         self._handler = handler
         self.sched_policy = sched_policy
-        self.uni_id = uni_id
         self._entity_id = None
-        self._free_alloc_id = OnuTCont.free_tcont_alloc_id
+        self._free_alloc_id = OnuTCont.FREE_TCONT_ALLOC_ID
 
     @property
     def entity_id(self):
@@ -54,7 +53,7 @@
                         td)
 
     @inlineCallbacks
-    def add_to_hardware(self, omci, tcont_entity_id, prev_alloc_id=free_tcont_alloc_id):
+    def add_to_hardware(self, omci, tcont_entity_id, prev_alloc_id=FREE_TCONT_ALLOC_ID):
         self.log.debug('add-to-hardware', tcont_entity_id=tcont_entity_id)
         if self._is_mock:
             returnValue('mock')
@@ -67,7 +66,7 @@
 
         try:
             # TODO: Look up ONU2-G QoS flexibility attribute and only set this
-            #       if it can be supported
+            #       if q-sched-policy  can be supported
 
             self._free_alloc_id = prev_alloc_id
             frame = TcontFrame(tcont_entity_id, self.alloc_id).set()