VOL-1257 - OpenOLT Driver Agent should support platforms-defined port topologies

Updated openolt driver initial activation sequence with BAL to query and
extract the topology information. This includes both number of ports as
well as the technology for each port. Presently, it is assumed that
all ports use same technology until the adapter resource manager supports
per-port technonologies. Updated hardcoded iterator extents to use
the dynamic results via NumPonIf_() and NumNniIf_(). Added construct
to allow vendors to specify their vendor/model name used in
DeviceInfo discovery.

Change-Id: I9050d78c3246d1be4e869ffdfb3a3f9314b9d959
diff --git a/Jenkinsfile b/Jenkinsfile
index 052f0d9..ff39856 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -26,10 +26,10 @@
           sh returnStdout: true, script: 'cp ../../build-files/OPENOLT_BAL_2.4.3.6.patch download'
         }
         stage ('Build packages and libraries') {
-          sh returnStdout: true, script: '/bin/bash -c make'
+          sh returnStdout: true, script: '/bin/bash -c ./configure && make DEVICE=asfvolt16'
         }
         stage ('Create Debian file') {
-          sh returnStdout: true, script: '/bin/bash -c "make deb"'
+          sh returnStdout: true, script: '/bin/bash -c "make DEVICE=asfvolt16 deb"'
         }
         stage ('Publish executables and DEB package to web server') {
           sh returnStdout: true, script: 'sudo mkdir -p /var/www/voltha-bal/executables'
diff --git a/Makefile.in b/Makefile.in
index 1f15760..7915e27 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -19,7 +19,8 @@
 ##        Config
 ##
 ##
-DEVICE ?= asfvolt16
+DEVICE ?= generic
+
 #
 # Three vendor proprietary source files are required to build BAL.
 # SW-BCM68620_<VER>.zip - Broadcom BAL source and Maple SDK.
@@ -63,6 +64,7 @@
 CXXFLAGS += -std=c++11 -fpermissive -Wno-literal-suffix
 LDFLAGS += @LDFLAGS@
 LDFLAGS += `pkg-config --libs protobuf grpc++ grpc` -ldl -lgpr
+CXXFLAGSDEVICE = -I./device -I./device/$(DEVICE) -I./device/generic
 
 export CXX CXXFLAGS
 
@@ -201,6 +203,18 @@
 ########################################################################
 ##
 ##
+##        device
+##
+##
+device/$(DEVICE)/%.o: device/$(DEVICE)/%.cc
+	$(CXX) $(CXXFLAGS) $(CXXFLAGSDEVICE) -c $< -o $@
+
+device/generic/%.o: device/generic/%.cc
+	$(CXX) $(CXXFLAGS) $(CXXFLAGSDEVICE) -c $< -o $@
+
+########################################################################
+##
+##
 ##        sim
 ##
 ##
@@ -221,7 +235,7 @@
 ##        openolt
 ##
 ##
-SRCS = $(wildcard src/*.cc) $(wildcard common/*.cc)
+SRCS = $(wildcard src/*.cc) $(wildcard common/*.cc) $(wildcard device/$(DEVICE)/*.cc)
 OBJS = $(SRCS:.cc=.o)
 DEPS = $(SRCS:.cc=.d)
 .DEFAULT_GOAL := all
@@ -234,7 +248,7 @@
 	ln -sf $(LIBGRPC_PATH)/libgrpc.so.6 $(BUILD_DIR)/libgrpc.so.6
 	ln -sf $(LIBGRPC_PATH)/libgrpc++.so.1 $(BUILD_DIR)/libgrpc++.so.1
 src/%.o: src/%.cc
-	$(CXX) $(CXXFLAGS) -I./common -c $< -o $@
+	$(CXX) $(CXXFLAGS) $(CXXFLAGSDEVICE) -I./common -c $< -o $@
 
 deb:
 	cp $(BUILD_DIR)/release_$(DEVICE)_V$(BAL_MAJOR_VER).$(DEV_VER).tar.gz mkdebian/debian
diff --git a/README.md b/README.md
index 8343375..557b16b 100644
--- a/README.md
+++ b/README.md
@@ -189,7 +189,7 @@
 This is usually a one-time thing, unless there is a change in the dependencies.
 
 ```shell
-make prereq
+make DEVICE=asfvolt16 prereq
 ```
 
 Run *make*. This can take a while to complete the first time, since it builds
@@ -197,7 +197,7 @@
 build the OpenOLT agent source.
 
 ```shell
-make
+make DEVICE=asfvolt16
 ```
 
 If the build process succeeds, libraries and executables will be created in the
@@ -206,7 +206,7 @@
 Optionally, build the debian package that will be installed on the OLT.
 
 ```shell
-make deb
+make DEVICE=asfvolt16 deb
 ```
 
 If the build process succeeds, the *openolt.deb* package will be created as
@@ -217,7 +217,7 @@
 To cleanup the repository and start the build procedure again, run:
 
 ```shell
-make clean-all
+make DEVICE=asfvolt16 clean-all
 ```
 
 ## FAQ
diff --git a/common/core.h b/common/core.h
index 78cbb0f..812a9b5 100644
--- a/common/core.h
+++ b/common/core.h
@@ -41,8 +41,12 @@
 Status DisablePonIf_(uint32_t intf_id);
 Status EnableUplinkIf_(uint32_t intf_id);
 Status DisableUplinkIf_(uint32_t intf_id);
+unsigned NumNniIf_();
+unsigned NumPonIf_();
 Status OmciMsgOut_(uint32_t intf_id, uint32_t onu_id, const std::string pkt);
 Status OnuPacketOut_(uint32_t intf_id, uint32_t onu_id, const std::string pkt);
+Status ProbeDeviceCapabilities_();
+Status ProbePonIfTechnology_();
 Status UplinkPacketOut_(uint32_t intf_id, const std::string pkt);
 Status FlowAdd_(int32_t onu_id,
                 uint32_t flow_id, const std::string flow_type,
diff --git a/common/main.cc b/common/main.cc
index 2898219..b211cfb 100644
--- a/common/main.cc
+++ b/common/main.cc
@@ -15,6 +15,7 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 #include <iostream>
+#include <unistd.h>
 
 #include "server.h"
 #include "core.h"
@@ -29,6 +30,18 @@
         return 1;
     }
 
+    // Wait for successful activation before allowing VOLTHA to connect. 
+    // This is necessary to allow the device topology to be dynamically
+    // queried from driver after initialization and activation is complete.
+    int maxTrials = 300;
+    while (!state.is_activated()) {
+        sleep(1);
+        if (--maxTrials == 0) {
+            std::cout << "ERROR: OLT/PON Activation failed" << std::endl;
+            return 1;
+        }
+    }
+
     RunServer();
 
     return 0;
diff --git a/device/asfvolt16/vendor.cc b/device/asfvolt16/vendor.cc
new file mode 100644
index 0000000..12aade8
--- /dev/null
+++ b/device/asfvolt16/vendor.cc
@@ -0,0 +1,22 @@
+/*
+    Copyright (C) 2018 Open Networking Foundation
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "vendor.h"
+
+void vendor_init()
+{
+}
diff --git a/device/asfvolt16/vendor.h b/device/asfvolt16/vendor.h
new file mode 100644
index 0000000..12cc60c
--- /dev/null
+++ b/device/asfvolt16/vendor.h
@@ -0,0 +1,22 @@
+/*
+    Copyright (C) 2018 Open Networking Foundation
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __VENDOR_H__
+#define __VENDOR_H__
+#define VENDOR_ID "EdgeCore"
+#define MODEL_ID  "asfvolt16"
+#endif
diff --git a/device/device.h b/device/device.h
new file mode 100644
index 0000000..fcaa887
--- /dev/null
+++ b/device/device.h
@@ -0,0 +1,25 @@
+/*
+    Copyright (C) 2018 Open Networking Foundation
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __DEVICE_H__
+#define __DEVICE_H__
+
+#include "vendor.h"
+
+extern void vendor_init();
+
+#endif
diff --git a/device/generic/vendor.cc b/device/generic/vendor.cc
new file mode 100644
index 0000000..12aade8
--- /dev/null
+++ b/device/generic/vendor.cc
@@ -0,0 +1,22 @@
+/*
+    Copyright (C) 2018 Open Networking Foundation
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "vendor.h"
+
+void vendor_init()
+{
+}
diff --git a/device/generic/vendor.h b/device/generic/vendor.h
new file mode 100644
index 0000000..3c32e43
--- /dev/null
+++ b/device/generic/vendor.h
@@ -0,0 +1,22 @@
+/*
+    Copyright (C) 2018 Open Networking Foundation
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __VENDOR_H__
+#define __VENDOR_H__
+#define VENDOR_ID "generic"
+#define MODEL_ID  "generic"
+#endif
diff --git a/device/tlabvolt8/vendor.cc b/device/tlabvolt8/vendor.cc
new file mode 100644
index 0000000..9de2b40
--- /dev/null
+++ b/device/tlabvolt8/vendor.cc
@@ -0,0 +1,22 @@
+/*
+    Copyright (C) 2018 Open Networking Foundation
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "device.h"
+
+void vendor_init()
+{
+}
diff --git a/device/tlabvolt8/vendor.h b/device/tlabvolt8/vendor.h
new file mode 100644
index 0000000..fb84611
--- /dev/null
+++ b/device/tlabvolt8/vendor.h
@@ -0,0 +1,22 @@
+/*
+    Copyright (C) 2018 Open Networking Foundation
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __VENDOR_H__
+#define __VENDOR_H__
+#define VENDOR_ID "Tellabs"
+#define MODEL_ID  "tlabvolt8"
+#endif
diff --git a/sim/core.cc b/sim/core.cc
index 7781ef9..cbbbaad 100644
--- a/sim/core.cc
+++ b/sim/core.cc
@@ -30,6 +30,9 @@
 State state;
 
 void* RunSim(void *) {
+
+    state.activate();
+
     while (!state.is_connected()) {
         sleep(5);
     }
diff --git a/src/core.cc b/src/core.cc
index 10ba783..10be676 100644
--- a/src/core.cc
+++ b/src/core.cc
@@ -25,11 +25,13 @@
 #include <chrono>
 #include <thread>
 
+#include "device.h"
 #include "core.h"
 #include "indications.h"
 #include "stats_collection.h"
 #include "error_format.h"
 #include "state.h"
+#include "utils.h"
 
 extern "C"
 {
@@ -44,10 +46,16 @@
 dev_log_id openolt_log_id = bcm_dev_log_id_register("OPENOLT", DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH);
 dev_log_id omci_log_id = bcm_dev_log_id_register("OMCI", DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH);
 
+#define MAX_SUPPORTED_INTF 16
+#define BAL_RSC_MANAGER_BASE_TM_SCHED_ID 16384
 
-#define NUM_OF_PON_PORTS 16
-const std::string technology = "xgspon";
-const std::string firmware_version = "BAL.2.6.0.1__Openolt.2018.09.10";
+static unsigned int num_of_nni_ports = 0;
+static unsigned int num_of_pon_ports = 0;
+static std::string intf_technology[MAX_SUPPORTED_INTF];
+static const std::string UNKNOWN_TECH("unknown");
+static std::string technology(UNKNOWN_TECH);
+
+static std::string firmware_version = "Openolt.2018.10.04";
 
 State state;
 
@@ -59,33 +67,50 @@
 }
 
 static inline int mk_agg_port_id(int intf_id, int onu_id) {
+    if (technology == "gpon") return 511 + intf_id * 32 + onu_id;
     return 1023 + intf_id * 32 + onu_id;
 }
 
-
 Status GetDeviceInfo_(openolt::DeviceInfo* device_info) {
-
-    device_info->set_vendor("EdgeCore");
-    device_info->set_model("asfvolt16");
+    device_info->set_vendor(VENDOR_ID);
+    device_info->set_model(MODEL_ID);
     device_info->set_hardware_version("");
     device_info->set_firmware_version(firmware_version);
     device_info->set_technology(technology);
-    device_info->set_pon_ports(NUM_OF_PON_PORTS);
-    device_info->set_onu_id_start(1);
-    device_info->set_onu_id_end(255);
-    device_info->set_alloc_id_start(1024);
-    device_info->set_alloc_id_end(16383);
-    device_info->set_gemport_id_start(1024);
-    device_info->set_gemport_id_end(65535);
+    device_info->set_pon_ports(num_of_pon_ports);
+    if (technology == "xgspon") {
+        device_info->set_onu_id_start(1);
+        device_info->set_onu_id_end(255);
+        device_info->set_alloc_id_start(1024);
+        device_info->set_alloc_id_end(16383);
+        device_info->set_gemport_id_start(1024);
+        device_info->set_gemport_id_end(65535);
+    }
+    else if (technology == "gpon") {
+        device_info->set_onu_id_start(0);
+        device_info->set_onu_id_end(127);
+        device_info->set_alloc_id_start(256);
+        device_info->set_alloc_id_end(767);
+        device_info->set_gemport_id_start(0);
+        device_info->set_gemport_id_end(4095);
+    }
+    else {
+        device_info->set_onu_id_start(0);
+        device_info->set_onu_id_end(0);
+        device_info->set_alloc_id_start(0);
+        device_info->set_alloc_id_end(0);
+        device_info->set_gemport_id_start(0);
+        device_info->set_gemport_id_end(0);
+    }
 
     // FIXME: Once dependency problem is fixed
-    // device_info->set_pon_ports(NUM_OF_PON_PORTS);
+    // device_info->set_pon_ports(num_of_pon_ports);
     // device_info->set_onu_id_end(XGPON_NUM_OF_ONUS - 1);
     // device_info->set_alloc_id_start(1024);
-    // device_info->set_alloc_id_end(XGPON_NUM_OF_ALLOC_IDS * NUM_OF_PON_PORTS ? - 1);
+    // device_info->set_alloc_id_end(XGPON_NUM_OF_ALLOC_IDS * num_of_pon_ports ? - 1);
     // device_info->set_gemport_id_start(XGPON_MIN_BASE_SERVICE_PORT_ID);
-    // device_info->set_gemport_id_end(XGPON_NUM_OF_GEM_PORT_IDS_PER_PON * NUM_OF_PON_PORTS ? - 1);
-    // device_info->set_pon_ports(NUM_OF_PON_PORTS);
+    // device_info->set_gemport_id_end(XGPON_NUM_OF_GEM_PORT_IDS_PER_PON * num_of_pon_ports ? - 1);
+    // device_info->set_pon_ports(num_of_pon_ports);
 
     return Status::OK;
 }
@@ -95,10 +120,12 @@
     bcmbal_access_terminal_key key = { };
 
     if (!state.is_activated()) {
-        BCM_LOG(INFO, openolt_log_id, "Enable OLT");
 
+        vendor_init();
         bcmbal_init(argc, argv, NULL);
 
+        BCM_LOG(INFO, openolt_log_id, "Enable OLT - %s-%s\n", VENDOR_ID, MODEL_ID);
+
         Status status = SubscribeIndication();
         if (!status.ok()) {
             BCM_LOG(ERROR, openolt_log_id, "SubscribeIndication failed - %s : %s\n",
@@ -116,6 +143,7 @@
             BCM_LOG(ERROR, openolt_log_id, "Failed to enable OLT\n");
             return bcm_to_grpc_err(err, "Failed to enable OLT");
         }
+
         init_stats();
     }
 
@@ -209,6 +237,103 @@
     return Status::OK;
 }
 
+Status ProbeDeviceCapabilities_() {
+    bcmbal_access_terminal_cfg acc_term_obj;
+    bcmbal_access_terminal_key key = { };
+
+    key.access_term_id = DEFAULT_ATERM_ID;
+    BCMBAL_CFG_INIT(&acc_term_obj, access_terminal, key);
+    BCMBAL_CFG_PROP_GET(&acc_term_obj, access_terminal, admin_state);
+    BCMBAL_CFG_PROP_GET(&acc_term_obj, access_terminal, oper_status);
+    BCMBAL_CFG_PROP_GET(&acc_term_obj, access_terminal, topology);
+    BCMBAL_CFG_PROP_GET(&acc_term_obj, access_terminal, sw_version);
+    BCMBAL_CFG_PROP_GET(&acc_term_obj, access_terminal, conn_id);
+    bcmos_errno err = bcmbal_cfg_get(DEFAULT_ATERM_ID, &(acc_term_obj.hdr));
+    if (err) {
+        BCM_LOG(ERROR, openolt_log_id, "Failed to query OLT\n");
+        return bcm_to_grpc_err(err, "Failed to query OLT");
+    }
+
+    BCM_LOG(INFO, openolt_log_id, "OLT capabilitites, admin_state: %s oper_state: %s\n", 
+            acc_term_obj.data.admin_state == BCMBAL_STATE_UP ? "up" : "down",
+            acc_term_obj.data.oper_status == BCMBAL_STATUS_UP ? "up" : "down");
+
+    std::string bal_version;
+    bal_version += std::to_string(acc_term_obj.data.sw_version.major_rev)
+                + "." + std::to_string(acc_term_obj.data.sw_version.minor_rev)
+                + "." + std::to_string(acc_term_obj.data.sw_version.release_rev);
+    firmware_version = "BAL." + bal_version + "__" + firmware_version;
+
+    BCM_LOG(INFO, openolt_log_id, "--------------- version %s object model: %d\n", bal_version.c_str(),
+            acc_term_obj.data.sw_version.om_version);
+
+    BCM_LOG(INFO, openolt_log_id, "--------------- topology nni:%d pon:%d dev:%d ppd:%d family: %d:%d\n",
+            acc_term_obj.data.topology.num_of_nni_ports,
+            acc_term_obj.data.topology.num_of_pon_ports,
+            acc_term_obj.data.topology.num_of_mac_devs,
+            acc_term_obj.data.topology.num_of_pons_per_mac_dev,
+            acc_term_obj.data.topology.pon_family,
+            acc_term_obj.data.topology.pon_sub_family
+            );
+
+    switch(acc_term_obj.data.topology.pon_sub_family)
+    {
+    case BCMBAL_PON_SUB_FAMILY_GPON:  technology = "gpon"; break;
+    case BCMBAL_PON_SUB_FAMILY_XGS:   technology = "xgspon"; break;
+    }
+
+    num_of_nni_ports = acc_term_obj.data.topology.num_of_nni_ports;
+    num_of_pon_ports = acc_term_obj.data.topology.num_of_pon_ports;
+
+    BCM_LOG(INFO, openolt_log_id, "PON num_intfs: %d global technology: %s\n", num_of_pon_ports, technology.c_str());
+
+    return Status::OK;
+}
+
+Status ProbePonIfTechnology_() {
+    // Probe maximum extent possible as configured into BAL driver to determine
+    // which are active in the current BAL topology. And for those
+    // that are active, determine each port's access technology, i.e. "gpon" or "xgspon".
+    for (uint32_t intf_id = 0; intf_id < num_of_pon_ports; ++intf_id) {
+        bcmbal_interface_cfg interface_obj;
+        bcmbal_interface_key interface_key;
+
+        interface_key.intf_id = intf_id;
+        interface_key.intf_type = BCMBAL_INTF_TYPE_PON;
+
+        BCMBAL_CFG_INIT(&interface_obj, interface, interface_key);
+        BCMBAL_CFG_PROP_GET(&interface_obj, interface, admin_state);
+        BCMBAL_CFG_PROP_GET(&interface_obj, interface, transceiver_type);
+
+        bcmos_errno err = bcmbal_cfg_get(DEFAULT_ATERM_ID, &(interface_obj.hdr));
+        if (err != BCM_ERR_OK) {
+            intf_technology[intf_id] = UNKNOWN_TECH;
+            if(err != BCM_ERR_RANGE) BCM_LOG(ERROR, openolt_log_id, "Failed to get PON config: %d\n", intf_id);
+        }
+        else {
+            switch(interface_obj.data.transceiver_type) {
+            case BCMBAL_TRX_TYPE_GPON_SPS_43_48:
+            case BCMBAL_TRX_TYPE_GPON_SPS_SOG_4321:
+            case BCMBAL_TRX_TYPE_GPON_LTE_3680_M:
+            case BCMBAL_TRX_TYPE_GPON_SOURCE_PHOTONICS:
+            case BCMBAL_TRX_TYPE_GPON_LTE_3680_P:
+                intf_technology[intf_id] = "gpon";
+                break;
+            default:
+                intf_technology[intf_id] = "xgspon";
+                break;
+            }
+            BCM_LOG(INFO, openolt_log_id, "PON intf_id: %d technology: %d:%s\n", intf_id, 
+                    interface_obj.data.transceiver_type, intf_technology[intf_id].c_str());
+        }
+    }
+
+    return Status::OK;
+}
+
+unsigned NumNniIf_() {return num_of_nni_ports;}
+unsigned NumPonIf_() {return num_of_pon_ports;}
+
 Status EnableUplinkIf_(uint32_t intf_id) {
     bcmbal_interface_cfg interface_obj;
     bcmbal_interface_key interface_key;
@@ -256,8 +381,8 @@
     bcmbal_serial_number serial_num = {};
     bcmbal_registration_id registration_id = {};
 
-    BCM_LOG(INFO, openolt_log_id,  "Enabling ONU %d on PON %d : vendor id %s, vendor specific %s, pir %d\n",
-        onu_id, intf_id, vendor_id, vendor_specific, pir);
+    BCM_LOG(INFO, openolt_log_id,  "Enabling ONU %d on PON %d : vendor id %s, vendor specific %s, pir %d, alloc_id %d\n",
+        onu_id, intf_id, vendor_id, vendor_specific_to_str(vendor_specific).c_str(), pir, alloc_id);
 
     subs_terminal_key.sub_term_id = onu_id;
     subs_terminal_key.intf_id = intf_id;
@@ -418,7 +543,7 @@
         BCM_LOG(ERROR, omci_log_id, "Error sending OMCI message to ONU %d on PON %d\n", onu_id, intf_id);
     } else {
         BCM_LOG(DEBUG, omci_log_id, "OMCI request msg of length %d sent to ONU %d on PON %d : %s\n",
-            buf.len, onu_id, intf_id, buf.val);
+            buf.len, onu_id, intf_id, pkt.c_str());
     }
 
     free(buf.val);
@@ -739,7 +864,7 @@
         BCMBAL_CFG_PROP_SET(&cfg, tm_sched, owner, owner);
 
         bcmbal_tm_sched_parent parent = { };
-        parent.sched_id = intf_id + 16384;
+        parent.sched_id = intf_id + BAL_RSC_MANAGER_BASE_TM_SCHED_ID;
         parent.presence_mask = parent.presence_mask | BCMBAL_TM_SCHED_PARENT_ID_SCHED_ID;
         parent.weight = 1;
         parent.presence_mask = parent.presence_mask | BCMBAL_TM_SCHED_PARENT_ID_WEIGHT;
diff --git a/src/indications.cc b/src/indications.cc
index 7a576de..b0ba964 100644
--- a/src/indications.cc
+++ b/src/indications.cc
@@ -40,26 +40,47 @@
 
 bcmos_errno OmciIndication(bcmbal_obj *obj);
 
-bcmos_errno OltIndication(bcmbal_obj *obj) {
+std::string bcmbal_to_grpc_intf_type(bcmbal_intf_type intf_type)
+{
+    if (intf_type == BCMBAL_INTF_TYPE_NNI) {
+        return "nni";
+    } else if (intf_type == BCMBAL_INTF_TYPE_PON) {
+        return "pon";
+    }
+    return "unknown";
+}
+
+bcmos_errno OltOperIndication(bcmbal_obj *obj) {
     openolt::Indication ind;
     openolt::OltIndication* olt_ind = new openolt::OltIndication;
     Status status;
 
     bcmbal_access_terminal_oper_status_change *acc_term_ind = (bcmbal_access_terminal_oper_status_change *)obj;
+    std::string admin_state;
+    if (acc_term_ind->data.admin_state == BCMBAL_STATE_UP) {
+        admin_state = "up";
+    } else {
+        admin_state = "down";
+    }
+
     if (acc_term_ind->data.new_oper_status == BCMBAL_STATUS_UP) {
+        // Determine device capabilities before transitionto acive state
+        ProbeDeviceCapabilities_();
         olt_ind->set_oper_state("up");
-        state.activate();
     } else {
         olt_ind->set_oper_state("down");
-        state.deactivate();
     }
     ind.set_allocated_olt_ind(olt_ind);
-    BCM_LOG(INFO, openolt_log_id, "Olt indication, oper_state: %s\n", ind.olt_ind().oper_state().c_str());
+
+    BCM_LOG(INFO, openolt_log_id, "Olt oper status indication, admin_state: %s oper_state: %s\n",
+            admin_state.c_str(),
+            olt_ind->oper_state().c_str());
+
     oltIndQ.push(ind);
 
-#define MAX_SUPPORTED_INTF 16
-    // Enable all PON interfaces.
-    for (int i = 0; i < MAX_SUPPORTED_INTF; i++) {
+    // Enable all PON interfaces. 
+    // 
+    for (int i = 0; i < NumPonIf_(); i++) {
         status = EnablePonIf_(i);
         if (!status.ok()) {
             // FIXME - raise alarm to report error in enabling PON
@@ -80,6 +101,14 @@
         bcmbal_subscribe_ind(0, &cb_cfg);
     }
 
+    if (acc_term_ind->data.new_oper_status == BCMBAL_STATUS_UP) {
+        ProbePonIfTechnology_();
+        state.activate();
+    }
+    else {
+        state.deactivate();
+    }
+
     return BCM_ERR_OK;
 }
 
@@ -92,7 +121,8 @@
     int intf_id = interface_key_to_port_no(bcm_los_ind->key);
     std::string status = alarm_status_to_string(bcm_los_ind->data.status);
 
-    BCM_LOG(INFO, openolt_log_id, "LOS indication : %d status %s\n", intf_id, status.c_str());
+    BCM_LOG(INFO, openolt_log_id, "LOS indication : intf_type: %d intf_id: %d port: %d status %s\n", 
+            bcm_los_ind->key.intf_type, bcm_los_ind->key.intf_id, intf_id, status.c_str());
 
     los_ind->set_intf_id(intf_id);
     los_ind->set_status(status);
@@ -104,51 +134,26 @@
     return BCM_ERR_OK;
 }
 
-bcmos_errno IfIndication(bcmbal_obj *obj) {
-    openolt::Indication ind;
-    openolt::IntfIndication* intf_ind = new openolt::IntfIndication;
-
-    BCM_LOG(INFO, openolt_log_id, "intf indication, intf_id: %d\n",
-        ((bcmbal_interface_oper_status_change *)obj)->key.intf_id );
-
-    intf_ind->set_intf_id(((bcmbal_interface_oper_status_change *)obj)->key.intf_id);
-    if (((bcmbal_interface_oper_status_change *)obj)->data.new_oper_status == BCMBAL_STATUS_UP) {
-        intf_ind->set_oper_state("up");
-    } else {
-        intf_ind->set_oper_state("down");
-    }
-    ind.set_allocated_intf_ind(intf_ind);
-
-    oltIndQ.push(ind);
-
-    return BCM_ERR_OK;
-}
-
 bcmos_errno IfOperIndication(bcmbal_obj *obj) {
     openolt::Indication ind;
     openolt::IntfOperIndication* intf_oper_ind = new openolt::IntfOperIndication;
-    BCM_LOG(INFO, openolt_log_id, "intf oper state indication, intf_id %d, type %d, oper_state %d, admin_state %d\n",
-        ((bcmbal_interface_oper_status_change *)obj)->key.intf_id,
-        ((bcmbal_interface_oper_status_change *)obj)->key.intf_type,
-        ((bcmbal_interface_oper_status_change *)obj)->data.new_oper_status,
-        ((bcmbal_interface_oper_status_change *)obj)->data.admin_state);
+    bcmbal_interface_oper_status_change* bcm_if_oper_ind = (bcmbal_interface_oper_status_change *) obj;
 
-    intf_oper_ind->set_intf_id(((bcmbal_interface_oper_status_change *)obj)->key.intf_id);
+    intf_oper_ind->set_type(bcmbal_to_grpc_intf_type(bcm_if_oper_ind->key.intf_type));
+    intf_oper_ind->set_intf_id(bcm_if_oper_ind->key.intf_id);
 
-    if (((bcmbal_interface_oper_status_change *)obj)->key.intf_type == BCMBAL_INTF_TYPE_NNI) {
-        intf_oper_ind->set_type("nni");
-    } else if (((bcmbal_interface_oper_status_change *)obj)->key.intf_type == BCMBAL_INTF_TYPE_PON) {
-        intf_oper_ind->set_type("pon");
-    } else {
-        intf_oper_ind->set_type("unknown");
-    }
-
-    if (((bcmbal_interface_oper_status_change *)obj)->data.new_oper_status == BCMBAL_STATUS_UP) {
+    if (bcm_if_oper_ind->data.new_oper_status == BCMBAL_STATUS_UP) {
         intf_oper_ind->set_oper_state("up");
     } else {
         intf_oper_ind->set_oper_state("down");
     }
 
+    BCM_LOG(INFO, openolt_log_id, "intf oper state indication, intf_type %s, intf_id %d, oper_state %d, admin_state %d\n",
+        intf_oper_ind->type().c_str(),
+        bcm_if_oper_ind->key.intf_id,
+        intf_oper_ind->oper_state().c_str(),
+        bcm_if_oper_ind->data.admin_state);
+
     ind.set_allocated_intf_oper_ind(intf_oper_ind);
 
     oltIndQ.push(ind);
@@ -223,7 +228,7 @@
     bcmbal_serial_number *in_serial_number = &(data->serial_number);
 
     BCM_LOG(INFO, openolt_log_id, "onu discover indication, intf_id %d, serial_number %s\n",
-        key->intf_id, serial_number_to_str(in_serial_number));
+        key->intf_id, serial_number_to_str(in_serial_number).c_str());
 
     onu_disc_ind->set_intf_id(key->intf_id);
     serial_number->set_vendor_id(reinterpret_cast<const char *>(in_serial_number->vendor_id), 4);
@@ -236,38 +241,6 @@
     return BCM_ERR_OK;
 }
 
-bcmos_errno OnuIndication(bcmbal_obj *obj) {
-    openolt::Indication ind;
-    openolt::OnuIndication* onu_ind = new openolt::OnuIndication;
-
-    bcmbal_subscriber_terminal_key *key =
-        &(((bcmbal_subscriber_terminal_oper_status_change*)obj)->key);
-
-    bcmbal_subscriber_terminal_oper_status_change_data *data =
-        &(((bcmbal_subscriber_terminal_oper_status_change*)obj)->data);
-
-    BCM_LOG(INFO, openolt_log_id, "onu indication, intf_id %d, onu_id %d, oper_state %d, admin_state %d\n",
-        key->intf_id, key->sub_term_id, data->new_oper_status, data->admin_state);
-
-    onu_ind->set_intf_id(key->intf_id);
-    onu_ind->set_onu_id(key->sub_term_id);
-    if (data->new_oper_status == BCMBAL_STATE_UP) {
-        onu_ind->set_oper_state("up");
-    } else {
-        onu_ind->set_oper_state("down");
-    }
-    if (data->admin_state == BCMBAL_STATE_UP) {
-        onu_ind->set_admin_state("up");
-    } else {
-        onu_ind->set_admin_state("down");
-    }
-
-    ind.set_allocated_onu_ind(onu_ind);
-
-    oltIndQ.push(ind);
-    return BCM_ERR_OK;
-}
-
 bcmos_errno OnuOperIndication(bcmbal_obj *obj) {
     openolt::Indication ind;
     openolt::OnuIndication* onu_ind = new openolt::OnuIndication;
@@ -278,10 +251,6 @@
     bcmbal_subscriber_terminal_oper_status_change_data *data =
         &(((bcmbal_subscriber_terminal_oper_status_change*)obj)->data);
 
-
-    BCM_LOG(INFO, openolt_log_id, "onu oper state indication, intf_id %d, onu_id %d, old oper state %d, new oper state %d\n",
-        key->intf_id, key->sub_term_id, data->old_oper_status, data->new_oper_status);
-
     onu_ind->set_intf_id(key->intf_id);
     onu_ind->set_onu_id(key->sub_term_id);
     if (data->new_oper_status == BCMBAL_STATE_UP) {
@@ -297,6 +266,9 @@
 
     ind.set_allocated_onu_ind(onu_ind);
 
+    BCM_LOG(INFO, openolt_log_id, "onu oper state indication, intf_id %d, onu_id %d, old oper state %d, new oper state %s, admin_state %s\n",
+        key->intf_id, key->sub_term_id, data->old_oper_status, onu_ind->oper_state().c_str(), onu_ind->admin_state().c_str());
+
     oltIndQ.push(ind);
     return BCM_ERR_OK;
 }
@@ -326,22 +298,17 @@
     openolt::PacketIndication* pkt_ind = new openolt::PacketIndication;
     bcmbal_packet_bearer_channel_rx *in = (bcmbal_packet_bearer_channel_rx *)obj;
 
-    BCM_LOG(INFO, openolt_log_id, "packet indication, intf_type %d, intf_id %d, svc_port %d, flow_id %d\n",
-        in->data.intf_type, in->data.intf_id, in->data.svc_port, in->data.flow_id);
-
-    if (in->data.intf_type == BCMBAL_INTF_TYPE_NNI) {
-        pkt_ind->set_intf_type("nni");
-    } else if (in->data.intf_type == BCMBAL_INTF_TYPE_PON) {
-        pkt_ind->set_intf_type("pon");
-    } else {
-        pkt_ind->set_intf_type("unknown");
-    }
+    pkt_ind->set_intf_type(bcmbal_to_grpc_intf_type(in->data.intf_type));
     pkt_ind->set_intf_id(in->data.intf_id);
     pkt_ind->set_gemport_id(in->data.svc_port);
     pkt_ind->set_flow_id(in->data.flow_id);
     pkt_ind->set_pkt(in->data.pkt.val, in->data.pkt.len);
 
     ind.set_allocated_pkt_ind(pkt_ind);
+
+    BCM_LOG(INFO, openolt_log_id, "packet indication, intf_type %s, intf_id %d, svc_port %d, flow_id %d\n",
+        pkt_ind->intf_type().c_str(), in->data.intf_id, in->data.svc_port, in->data.flow_id);
+
     oltIndQ.push(ind);
 
     return BCM_ERR_OK;
@@ -587,14 +554,13 @@
 
     cb_cfg.module = BCMOS_MODULE_ID_NONE;
 
-
-    /* OLT device indication */
+    /* OLT device operational state change indication */
     cb_cfg.obj_type = BCMBAL_OBJ_ID_ACCESS_TERMINAL;
     ind_subgroup = bcmbal_access_terminal_auto_id_oper_status_change;
     cb_cfg.p_subgroup = &ind_subgroup;
-    cb_cfg.ind_cb_hdlr = (f_bcmbal_ind_handler)OltIndication;
+    cb_cfg.ind_cb_hdlr = (f_bcmbal_ind_handler)OltOperIndication;
     if (BCM_ERR_OK != bcmbal_subscribe_ind(DEFAULT_ATERM_ID, &cb_cfg)) {
-        return Status(grpc::StatusCode::INTERNAL, "Olt indication subscribe failed");
+        return Status(grpc::StatusCode::INTERNAL, "Olt operations state change indication subscribe failed");
     }
 
     /* Interface LOS indication */
@@ -606,15 +572,6 @@
         return Status(grpc::StatusCode::INTERNAL, "LOS indication subscribe failed");
     }
 
-    /* Interface indication */
-    cb_cfg.obj_type = BCMBAL_OBJ_ID_INTERFACE;
-    ind_subgroup = bcmbal_interface_auto_id_oper_status_change;
-    cb_cfg.p_subgroup = &ind_subgroup;
-    cb_cfg.ind_cb_hdlr = (f_bcmbal_ind_handler)IfIndication;
-    if (BCM_ERR_OK != bcmbal_subscribe_ind(DEFAULT_ATERM_ID, &cb_cfg)) {
-        return Status(grpc::StatusCode::INTERNAL, "Interface indication subscribe failed");
-    }
-
     /* Interface operational state change indication */
     cb_cfg.obj_type = BCMBAL_OBJ_ID_INTERFACE;
     ind_subgroup = bcmbal_interface_auto_id_oper_status_change;
@@ -651,14 +608,6 @@
         return Status(grpc::StatusCode::INTERNAL, "onu discovery indication subscribe failed");
     }
 
-    /* onu indication */
-    cb_cfg.obj_type = BCMBAL_OBJ_ID_SUBSCRIBER_TERMINAL;
-    ind_subgroup = bcmbal_subscriber_terminal_auto_id_oper_status_change;
-    cb_cfg.p_subgroup = &ind_subgroup;
-    cb_cfg.ind_cb_hdlr = (f_bcmbal_ind_handler)OnuIndication;
-    if (BCM_ERR_OK != bcmbal_subscribe_ind(DEFAULT_ATERM_ID, &cb_cfg)) {
-        return Status(grpc::StatusCode::INTERNAL, "onu indication subscribe failed");
-    }
     /* onu operational state change indication */
     cb_cfg.obj_type = BCMBAL_OBJ_ID_SUBSCRIBER_TERMINAL;
     ind_subgroup = bcmbal_subscriber_terminal_auto_id_oper_status_change;
diff --git a/src/stats_collection.cc b/src/stats_collection.cc
index 8afc5f2..e988d44 100644
--- a/src/stats_collection.cc
+++ b/src/stats_collection.cc
@@ -164,7 +164,7 @@
     //Ports statistics
 
     //Uplink ports
-    for (int i = 0; i < 4; i++) {
+    for (int i = 0; i < NumNniIf_(); i++) {
         bcmbal_interface_key nni_interface;
         nni_interface.intf_type = BCMBAL_INTF_TYPE_NNI;
         nni_interface.intf_id = i;
@@ -176,7 +176,7 @@
         oltIndQ.push(ind);
     }
     //Pon ports
-    for (int i = 0; i < 16; i++) {
+    for (int i = 0; i < NumPonIf_(); i++) {
         bcmbal_interface_key pon_interface;
         pon_interface.intf_type = BCMBAL_INTF_TYPE_PON;
         pon_interface.intf_id = i;
diff --git a/src/utils.cc b/src/utils.cc
index 9610cdc..0ddff3f 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -17,10 +17,9 @@
 
 #include "utils.h"
 
-// NOTE - This function is not thread-safe.
-const char* serial_number_to_str(bcmbal_serial_number* serial_number) {
+std::string serial_number_to_str(bcmbal_serial_number* serial_number) {
 #define SERIAL_NUMBER_SIZE 12
-    static char buff[SERIAL_NUMBER_SIZE+1];
+    char buff[SERIAL_NUMBER_SIZE+1];
 
     sprintf(buff, "%c%c%c%c%1X%1X%1X%1X%1X%1X%1X%1X",
             serial_number->vendor_id[0],
@@ -38,3 +37,20 @@
 
     return buff;
 }
+
+std::string vendor_specific_to_str(char const * const vendor_specific) {
+    char buff[SERIAL_NUMBER_SIZE+1];
+
+    sprintf(buff, "%1X%1X%1X%1X%1X%1X%1X%1X",
+            vendor_specific[0]>>4 & 0x0f,
+            vendor_specific[0] & 0x0f,
+            vendor_specific[1]>>4 & 0x0f,
+            vendor_specific[1] & 0x0f,
+            vendor_specific[2]>>4 & 0x0f,
+            vendor_specific[2] & 0x0f,
+            vendor_specific[3]>>4 & 0x0f,
+            vendor_specific[3] & 0x0f);
+
+    return buff;
+}
+
diff --git a/src/utils.h b/src/utils.h
index 300bcbb..53f4a99 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -18,6 +18,8 @@
 #ifndef OPENOLT_UTILS_H_
 #define OPENOLT_UTILS_H_
 
+#include <string>
+
 extern "C"
 {
 #include <bcmos_system.h>
@@ -25,6 +27,7 @@
 #include <bal_api_end.h>
 }
 
-const char* serial_number_to_str(bcmbal_serial_number* serial_number);
+std::string serial_number_to_str(bcmbal_serial_number* serial_number);
+std::string vendor_specific_to_str(const char* const serial_number);
 
 #endif