[VOL-4676]:
Initial Framework for reading SFP capabilities by reading the EEPROM data.
Use the detected SFP data to derive the MAC and PON system mode.
Make the SFP EEPROM read mode configurable per platform through the
DYNAMIC_PON_TRX_SUPPORT '#define' defined in the platform vendor.h file.

Change-Id: I07d7763371d2f804a1e93ca38646b1a30198f8ee
diff --git a/Makefile b/Makefile
index 70c5cc6..8bf4bc1 100644
--- a/Makefile
+++ b/Makefile
@@ -44,6 +44,9 @@
 test:
 	${DOCKER} run --rm -v $(shell pwd):/app $(shell test -t 0 && echo "-it") ${IMAGENAME} make -C agent/test test
 
+build:
+	${DOCKER} run --rm -v $(shell pwd):/app $(shell test -t 0 && echo "-it") ${IMAGENAME} make -C agent/test all
+
 clean:
 	${DOCKER} run --rm -v $(shell pwd):/app $(shell test -t 0 && echo "-it") ${IMAGENAME} make -C agent/test clean
 
diff --git a/README.md b/README.md
index 82f2293..b69fdf5 100644
--- a/README.md
+++ b/README.md
@@ -291,6 +291,34 @@
 
 To enable TLS encryption features with td-agent [look at this google doc](https://docs.google.com/document/d/1KF1HhE-PN-VY4JN2bqKmQBrZghFC5HQM_s0mC0slapA/edit)
 
+## Supporing Dynamic PON Transceiver (TRX) detection and configuration of MAC and PON mode
+
+The `PonTrxBase` class defined in `agent/device/device.(h/cc)` provides base implementation with the following capabilities
+- Read SFP/Trx presence information using the `onlpdump` utility on the OLT
+- Read EEPROM data for the PON Trx
+- Decode PON Trx EEPROM data for Vendor Name, P/N, Rev, OUI and Wavelength(s)
+- Get SFP mode
+- Get MAC mode
+- Store SFP, EEPROM data
+
+The base class implentation has to derived class `PonTrx` defined in vendor specific modules and if needed override the base class methods to provide vendor specific implementation. See `agent/device/sim/vendor.(h/cc)` for how this is done for the simulated vendor used in unit tests.
+
+The usual steps involved in openolt-agent startup is below
+1. Read and populate SFP presence data (check `read_sfp_presence_data`)
+2. Read EEPROM data for all the detected SFPs (check `read_eeprom_data_for_sfp`)
+3. Decode EEPROM data for the successfully read SFP EEPROM data (check `decode_eeprom_data`)
+4. Then use `get_mac_system_mode` and `get_sfp_mode` to get the modes while configuring the MAC and the PON respectively.
+
+If any vendor does not want to support dynamic MAC and PON mode configuration, then do not define `DYNAMIC_PON_TRX_SUPPORT` in `agent/device/<vendor-folder>/vendor.h` and then provide the definition of `DEFAULT_MAC_SYSTEM_MODE` and `DEFAULT_PON_MODE` that you would want to use for your device.
+
+### What is supported today?
+All that is described in the earlier section is supported today for supporting Dynamic PON Trx detection and configuration of MAC and PON mode.
+
+### What is missing?
+- Static configuration of PON Trx modes via a yaml configuration file that is discussed in this document https://docs.google.com/document/d/129YDzShMvYACsrM0dWV60tV79BRPI6SsBpf_nm0Byxc/edit#heading=h.f2l6z075wl6e
+- Hot plug in/out of PON Trx and then reconfiguration of MAC and PON modes
+- More error/exception handling in the `PonTrxBase` implementation. But this, as the name suggests, is a base class implementation - could always be overriden in the derived class if you need a sophesticated implementation.
+- Test and enhance for Combo PON SFPs. Currently only tested for either GPON or XGSPON SFP.
 
 ## Build OpenOLT Agent
 
@@ -452,3 +480,4 @@
 Bandwidth-Profiles-for-Ethernet-Services](https://wiki.mef.net/display/CESG/Bandwidth+Profile)
 [Technology Profile Implementation
 Note](https://www.opennetworking.org/wp-content/uploads/2019/09/2pm-Shaun-Missett-Technology-Profile-and-Speed-Profile-Implementation.pdf)
+
diff --git a/VERSION b/VERSION
index b0453d9..0b2eb36 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.7.2-dev
+3.7.2
diff --git a/agent/Makefile.in b/agent/Makefile.in
index 494bc81..89967fe 100644
--- a/agent/Makefile.in
+++ b/agent/Makefile.in
@@ -373,10 +373,10 @@
 ##        device
 ##
 ##
-device/$(OPENOLTDEVICE)/%.o: device/$(OPENOLTDEVICE)/%.cc
-	$(CXX) $(CXXFLAGS) $(CXXFLAGSDEVICE) -c $< -o $@
+device/%.o: device/%.cc
+	$(CXX) $(CXXFLAGS) $(CXXFLAGSDEVICE) -I./common -I./src -c $< -o $@
 
-device/generic/%.o: device/generic/%.cc
+device/$(OPENOLTDEVICE)/%.o: device/$(OPENOLTDEVICE)/%.cc
 	$(CXX) $(CXXFLAGS) $(CXXFLAGSDEVICE) -c $< -o $@
 
 ########################################################################
@@ -386,7 +386,7 @@
 ##
 ##
 OLT_SW_UPGRADE_DIR = ../olt-sw-upgrade
-SRCS = $(wildcard src/*.cc) $(wildcard common/*.cc) $(wildcard device/$(OPENOLTDEVICE)/*.cc)
+SRCS = $(wildcard src/*.cc) $(wildcard common/*.cc) $(wildcard device/*.cc) $(wildcard device/$(OPENOLTDEVICE)/*.cc)
 ANSIBLE_DIR = /etc/ansible
 OBJS = $(SRCS:.cc=.o)
 DEPS = $(SRCS:.cc=.d)
diff --git a/agent/common/core.h b/agent/common/core.h
index 2159716..4279f35 100644
--- a/agent/common/core.h
+++ b/agent/common/core.h
@@ -23,6 +23,7 @@
 #include <voltha_protos/ext_config.grpc.pb.h>
 
 #include "state.h"
+#include "vendor.h"
 
 extern "C"
 {
@@ -199,6 +200,7 @@
 
 #define LOGICAL_DISTANCE(MLD,EQD,TD) (MLD-(EQD*TD)*102) /* Round-trip time of 102 meters is 1us */
 extern State state;
+extern PonTrx ponTrx;
 
 //***************************************//
 // Function declations used by the core. //
diff --git a/agent/common/main.cc b/agent/common/main.cc
index ce55e4d..8e0aba8 100644
--- a/agent/common/main.cc
+++ b/agent/common/main.cc
@@ -65,12 +65,12 @@
 
     for (int i = 1; i < argc; ++i) {
         if(strcmp(argv[i], "--version") == 0 || (strcmp(argv[i], "-v") == 0)) {
-           std::cout << "OpenOLT agent: " << version << "\n";
-           std::cout << "BAL version: " << bal_version << "\n";
-           std::cout << "Label VCS Url: " << label_vcs_url << "\n";
-           std::cout << "Label VCS Ref: " << label_vcs_ref << "\n";
-           std::cout << "Label build date: " << label_build_date << "\n";
-           std::cout << "Label commit date: " << label_commit_date << "\n";
+           cout << "OpenOLT agent: " << version << "\n";
+           cout << "BAL version: " << bal_version << "\n";
+           cout << "Label VCS Url: " << label_vcs_url << "\n";
+           cout << "Label VCS Ref: " << label_vcs_ref << "\n";
+           cout << "Label build date: " << label_build_date << "\n";
+           cout << "Label commit date: " << label_commit_date << "\n";
            exit(0);
         }
     }
@@ -80,11 +80,32 @@
 
     display_version_info(argc, argv);
 
+#ifdef DYNAMIC_PON_TRX_SUPPORT
+    auto sfp = ponTrx.read_sfp_presence_data();
+    if (sfp.size() == 0) {
+        perror("sfp presence map could not be read\n");
+        return 2;
+    }
+    for (const auto& it : sfp) {
+        bool res = ponTrx.read_eeprom_data_for_sfp(it);
+        if (!res) {
+            cerr << "eeprom data for sfp could not be read: " << it << endl;
+            return 2;
+        }
+    }
+    for (const auto &it : sfp) {
+        bool res = ponTrx.decode_eeprom_data(it);
+        if (!res) {
+            cerr << "eeprom data for sfp could not be decoded: " << it << endl;
+            return 2;
+        }
+    }
+#endif
     Status status = Enable_(argc, argv);
     if (!status.ok()) {
-        std::cout << "ERROR: Enable_ failed - "
+        cout << "ERROR: Enable_ failed - "
                   << status.error_code() << ": " << status.error_message()
-                  << std::endl;
+                  << endl;
         return 1;
     }
 
@@ -95,14 +116,14 @@
     while (!state.is_activated()) {
         sleep(1);
         if (--maxTrials == 0) {
-            std::cout << "ERROR: OLT/PON Activation failed" << std::endl;
+            cout << "ERROR: OLT/PON Activation failed" << endl;
             return 1;
         }
     }
 
     status = ProbeDeviceCapabilities_();
     if (!status.ok()) {
-        std::cout << "ERROR: Could not find the OLT Device capabilities" << std::endl;
+        cout << "ERROR: Could not find the OLT Device capabilities" << endl;
         return 1;
     }
 
@@ -149,7 +170,7 @@
     }
 
     if (!RunServer(argc, argv)) {
-        std::cerr << "FATAL: gRPC server creation failed\n";
+        cerr << "FATAL: gRPC server creation failed\n";
         return 2;
     }
 
diff --git a/agent/device/asfvolt16/vendor.cc b/agent/device/asfvolt16/vendor.cc
index 86836ff..1c88cb0 100644
--- a/agent/device/asfvolt16/vendor.cc
+++ b/agent/device/asfvolt16/vendor.cc
@@ -19,3 +19,13 @@
 void vendor_init()
 {
 }
+
+// Returns the MAC System Mode based on the set of SFP IDs provided.
+// The derived class will most likely need to override this method to provide a
+// different implementation for that particular OLT platform.
+pair<bcmolt_system_mode, bool> PonTrx::get_mac_system_mode(int olt_mac_id, set<int> sfp_ids) {
+    bool ret = true;
+    bcmolt_system_mode sm = BCMOLT_SYSTEM_MODE_XGS__2_X;
+
+    return {sm, ret};
+}
\ No newline at end of file
diff --git a/agent/device/asfvolt16/vendor.h b/agent/device/asfvolt16/vendor.h
index f63142e..2e6c499 100644
--- a/agent/device/asfvolt16/vendor.h
+++ b/agent/device/asfvolt16/vendor.h
@@ -16,12 +16,15 @@
 
 #ifndef __VENDOR_H__
 #define __VENDOR_H__
+#include "device.h"
 #define VENDOR_ID "EdgeCore"
 #define ASFVOLT16
 #define MODEL_ID  "asfvolt16"
 #define MAX_SUPPORTED_PON 16
 #define ONU_BIT_TRANSMISSION_DELAY 0.1004823/1000 /* unit: ns to us */
 #define MINIMUM_ONU_RESPONSE_RANGING_TIME 1572135 /* hardcore: this is ranging time for the shortest distance, typically 35us */
+#define DEFAULT_MAC_SYSTEM_MODE BCMOLT_SYSTEM_MODE_XGS__2_X
+#define DEFAULT_PON_MODE BCMOLT_PON_TYPE_XGPON
 
 // DeviceInfo definitions
 
@@ -44,4 +47,59 @@
 
 #define MAC_DEVICE_ACTIVATION_DELAY 2000000 // in microseconds
 
+///////////////////////////////////////////////////////
+// Constants relevant for decoding PON Trx EEPROM Data
+
+// Uncomment below line when you need dynamic transceiver detection support
+// #define DYNAMIC_PON_TRX_SUPPORT
+
+
+#define TOTAL_PON_TRX_PORTS 16 // total PON transceiver ports
+#define TOTAL_PON_PORTS 16 // total PON ports (we could have up to 2 PON ports on the OLT MAC mapped to the external PON Trx)
+const int trx_port_to_pon_port_map[TOTAL_PON_TRX_PORTS][TOTAL_PON_PORTS/TOTAL_PON_TRX_PORTS]={{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},
+{11},{12},{13},{14},{15}};
+#define PONS_PER_TRX (TOTAL_PON_TRX_PORTS/TOTAL_PON_PORTS) // if there are more than one OLT MAC device,
+                                                           // they all have to be of the same type for this to work.
+
+const int bus_index[TOTAL_PON_TRX_PORTS] = {
+   47,  48,  37,  38,  35,  36,  33,  34,
+   39,  40,  41,  42,  43,  44,  45,  46
+};
+
+
+// FIXME: Check the correctness of the below constants for this platform
+
+#define PORT_ADDRESS 50
+
+#define NAME_EEPROM "sfp_eeprom"
+
+#define EEPROM_VENDOR_NAME_START_IDX 148
+#define EEPROM_VENDOR_NAME_LENGTH 16
+
+#define EEPROM_VENDOR_OUI_START_IDX 165
+#define EEPROM_VENDOR_OUI_LENGTH 3
+
+#define EEPROM_VENDOR_PART_NUMBER_START_IDX 168
+#define EEPROM_VENDOR_PART_NUMBER_LENGTH 16
+
+#define EEPROM_VENDOR_REVISION_START_IDX 184
+#define EEPROM_VENDOR_REVISION_LENGTH 2
+
+#define EEPROM_DOWNSTREAM_WAVELENGTH_START_IDX 186
+#define EEPROM_DOWNSTREAM_WAVELENGTH_LENGTH 2
+#define EEPROM_WAVELENGTH_RESOLUTION 0.05
+
+// Define valid values below in case of Combo PON Trx is supported
+// #define EEPROM_DOWNSTREAM_SECONDARY_WAVELENGTH_START_IDX 120
+// #define EEPROM_DOWNSTREAM_SECONDARY_WAVELENGTH_LENGTH 2
+
+
+class PonTrx: public PonTrxBase {
+   // override the base member functions if you need a different implementation
+   public:
+      // Get MAC System mode based on the olt mac id and the set of SFP IDs provided
+      pair<bcmolt_system_mode, bool> get_mac_system_mode(int, set<int>);
+
+};
+
 #endif
diff --git a/agent/device/asgvolt64/mkdebian/debian/files b/agent/device/asgvolt64/mkdebian/debian/files
index f622278..4a4e596 100644
--- a/agent/device/asgvolt64/mkdebian/debian/files
+++ b/agent/device/asgvolt64/mkdebian/debian/files
@@ -1 +1 @@
-asgvolt64_3.10.2.2+edgecore-V202111090101_amd64.deb misc optional
+asgvolt64_3.10.2.2+edgecore-V202111040101_amd64.deb misc optional
diff --git a/agent/device/asgvolt64/vendor.cc b/agent/device/asgvolt64/vendor.cc
index 86836ff..7e68606 100644
--- a/agent/device/asgvolt64/vendor.cc
+++ b/agent/device/asgvolt64/vendor.cc
@@ -18,4 +18,4 @@
 
 void vendor_init()
 {
-}
+}
\ No newline at end of file
diff --git a/agent/device/asgvolt64/vendor.h b/agent/device/asgvolt64/vendor.h
index 89a4bff..7f49bc5 100644
--- a/agent/device/asgvolt64/vendor.h
+++ b/agent/device/asgvolt64/vendor.h
@@ -16,12 +16,18 @@
 
 #ifndef __VENDOR_H__
 #define __VENDOR_H__
+#include "device.h"
+
 #define VENDOR_ID "EdgeCore"
 #define ASGVOLT64
 #define MODEL_ID  "asgvolt64"
 #define MAX_SUPPORTED_PON 64
 #define ONU_BIT_TRANSMISSION_DELAY 0.8038585/1000 /* unit: ns to us */
 #define MINIMUM_ONU_RESPONSE_RANGING_TIME 198075 /* hardcore: this is ranging time for the shortest distance, typically 35us */
+#define DEFAULT_MAC_SYSTEM_MODE BCMOLT_SYSTEM_MODE_GPON__16_X
+//#define DEFAULT_MAC_SYSTEM_MODE BCMOLT_SYSTEM_MODE_XGS__2_X
+#define DEFAULT_PON_MODE BCMOLT_PON_TYPE_GPON
+// #define DEFAULT_PON_MODE BCMOLT_PON_TYPE_XGPON
 
 // DeviceInfo definitions
 
@@ -44,4 +50,64 @@
 
 #define MAC_DEVICE_ACTIVATION_DELAY 2000000 // in microseconds
 
+///////////////////////////////////////////////////////
+// Constants relevant for decoding PON Trx EEPROM Data
+
+// Uncomment below line when you need dynamic transceiver detection support
+//#define DYNAMIC_PON_TRX_SUPPORT
+
+#define TOTAL_PON_TRX_PORTS 64 // total PON transceiver ports
+#define TOTAL_PON_PORTS 64 // total PON ports (we could have up to 2 PON ports on the OLT MAC mapped to the external PON Trx)
+const int trx_port_to_pon_port_map[TOTAL_PON_TRX_PORTS][TOTAL_PON_PORTS/TOTAL_PON_TRX_PORTS]={{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},
+{11},{12},{13},{14},{15},{16},{17},{18},{19},{20},{21},{22},{23},{24},{25},{26},{27},{28},{29},{30},{31},{32},{33},{34},{35},{36},{37},{38},
+{39},{40},{41},{42},{43},{44},{45},{46},{47},{48},{49},{50},{51},{52},{53},{54},{55},{56},{57},{58},{59},{60},{61},{62},{63}};
+#define PONS_PER_TRX (TOTAL_PON_TRX_PORTS/TOTAL_PON_PORTS) // if there are more than one OLT MAC device,
+                                                           // they all have to be of the same type for this to work.
+
+const int bus_index[TOTAL_PON_TRX_PORTS] = {
+   41,  42,  56,  55,  43,  44,  54,  53,
+   45,  46,  52,  51,  47,  48,  50,  49,
+   57,  58,  72,  71,  59,  60,  70,  69,
+   61,  62,  68,  67,  63,  64,  66,  65,
+   73,  74,  88,  87,  75,  76,  86,  85,
+   77,  78,  84,  83,  79,  80,  82,  81,
+   89,  90, 104, 103,  91,  92, 102, 101,
+   93,  94, 100,  99,  95,  96,  98,  97
+};
+
+// FIXME: Check the correctness of the below constants for this platform
+
+#define PORT_ADDRESS 50
+
+#define NAME_EEPROM "eeprom"
+
+#define EEPROM_VENDOR_NAME_START_IDX 20
+#define EEPROM_VENDOR_NAME_LENGTH 16
+
+#define EEPROM_VENDOR_OUI_START_IDX 37
+#define EEPROM_VENDOR_OUI_LENGTH 3
+
+#define EEPROM_VENDOR_PART_NUMBER_START_IDX 40
+#define EEPROM_VENDOR_PART_NUMBER_LENGTH 16
+
+#define EEPROM_VENDOR_REVISION_START_IDX 56
+#define EEPROM_VENDOR_REVISION_LENGTH 4
+
+#define EEPROM_DOWNSTREAM_WAVELENGTH_START_IDX 60
+#define EEPROM_DOWNSTREAM_WAVELENGTH_LENGTH 2
+#define EEPROM_WAVELENGTH_RESOLUTION 1
+
+// Define valid values below in case of Combo PON Trx is supported
+// #define EEPROM_DOWNSTREAM_SECONDARY_WAVELENGTH_START_IDX 120
+// #define EEPROM_DOWNSTREAM_SECONDARY_WAVELENGTH_LENGTH 2
+
+class PonTrx: public PonTrxBase {
+   public:
+      // override the base member functions if you need a different implementation
+      PonTrx(): PonTrxBase("eeprom", 50, "/sys/bus/i2c/devices/%d-00%d/%s") {
+
+      }
+
+};
+
 #endif
diff --git a/agent/device/device.cc b/agent/device/device.cc
new file mode 100644
index 0000000..c6f4cbf
--- /dev/null
+++ b/agent/device/device.cc
@@ -0,0 +1,369 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+
+ * 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.
+ */
+
+#include "device.h"
+#include "vendor.h"
+#include "core_utils.h"
+
+using namespace std;
+
+// PonTrxBase class member function definitions
+// Note: 'cout' logger has been used in this file instead of OPENOLT_LOG
+// because the logger task may not be initialized in the openolt core module
+// before PonTrx module is initialized in the main handler. In that case,
+// any OPENOLT_LOG logger (which internally uses BCM_LOG) will not be logged.
+
+PonTrxBase::PonTrxBase(string eeprom_file_name,
+                       int port_addr,
+                       string eeprom_file_path_name,
+                       string sfp_presence_command) :
+        _eeprom_file_name{eeprom_file_name},
+        _port_addr{port_addr},
+        _eeprom_file_path_format{eeprom_file_path_name},
+        _sfp_presence_command{sfp_presence_command}
+{
+}
+
+PonTrxBase::~PonTrxBase() {
+    for (auto it : _t_data) {
+        delete it;
+    }
+}
+
+// returns the _sfp_presence_data
+const set<int> PonTrxBase::get_sfp_presence_data() {
+    return _sfp_presence_data;
+}
+
+// reads, updates and returns the _sfp_presence_data
+const set<int> PonTrxBase::read_sfp_presence_data() {
+    set<int> sp;
+    // command to read the sfp presence array
+    // The sample output of the command looks like below
+    // root@localhost:~# onlpdump -p
+    // Presence: 0 1 16
+    const string& command = _sfp_presence_command;
+    int status_code = system((command + " > temp.txt").c_str());
+    if (status_code != 0) {
+        perror("error getting sfp presence array from ONL\n");
+        // return the current _sfp_presence_data
+        return _sfp_presence_data;
+    }
+    ifstream ifs("temp.txt");
+    string res = {istreambuf_iterator<char>(ifs), istreambuf_iterator<char>()};
+    ifs.close(); // must close the inout stream so the file can be cleaned up
+    // If there is issue reading the sfp presence array, print error and return existing data
+    if (remove("temp.txt") != 0) {
+        perror("Error deleting temporary file");
+        // return the current sfp presence array
+        return _sfp_presence_data;
+    }
+
+    // parse the output of sfp presence command and read which PON Trx are connected/disconnected now
+    stringstream ss(res);
+    string s;
+    while (getline(ss, s, ' ')) {
+        try {
+            int i = stoi(s);
+            // Set the Trx port index corresponding the PON port to true if it appears on the sfp presence command output
+            if (i < TOTAL_PON_TRX_PORTS) {
+                sp.insert(i);
+            }
+
+        } catch(invalid_argument) {
+            // do nothing
+        } catch(out_of_range) {
+            // do nothing
+        }
+    }
+
+    // If the current _sfp_presence_data is not same the newly read data,
+    // update local copy and also raise an event.
+    if (sp != _sfp_presence_data) {
+        cout << "sfp presence data has been updated\n";
+
+        set<int> sfp_ports_up, sfp_ports_down;
+
+        set_difference(sp.begin(), sp.end(), _sfp_presence_data.begin(), _sfp_presence_data.end(), inserter(sfp_ports_up, sfp_ports_up.begin()));
+        // New SFPs are detected
+        if (sfp_ports_up.size() > 0) {
+            cout << "following pon sfp ports are up: ";
+            for (auto up_it : sfp_ports_up) {
+                cout << up_it << " ";
+            }
+            cout << "\n";
+        }
+        set_difference(_sfp_presence_data.begin(), _sfp_presence_data.end(), sp.begin(), sp.end(), inserter(sfp_ports_down, sfp_ports_down.begin()));
+        // Some SFPs are down
+        if (sfp_ports_down.size() > 0) {
+            cout << "following pon sfp ports are down: \n";
+            for (auto down_it : sfp_ports_down) {
+                cout << down_it << " ";
+            }
+            cout << "\n";
+        }
+
+        // FIXME: raise an event for all the PON Trx ports that have been updated
+        _sfp_presence_data = sp;
+    }
+
+    return _sfp_presence_data;
+}
+
+// reads the EEPROM data. The sfp_index is the bus index corresponding to the PON Trx
+// returns true if success
+bool PonTrxBase::read_eeprom_data_for_sfp(int sfp_index) {
+    ifstream is;
+    int len;
+    array<char, EEPROM_READ_PATH_SIZE> path{};
+    array<unsigned char, EEPROM_DATA_READ_SIZE> data{};
+
+    if (sfp_index >= TOTAL_PON_TRX_PORTS) {
+        perror("invalid pon trx index\n");
+        return false;
+    }
+
+    if (_eeprom_read_path.count(sfp_index) == 0) {
+        _eeprom_read_path[sfp_index] = path;
+        char file_name[PON_TRX_BUS_FORMAT_SIZE];
+        char *tmp_place_holder = new char[PON_TRX_BUS_FORMAT_SIZE];
+        // 4 is max length of _port_addr
+        if ((_eeprom_file_path_format.length() + _eeprom_file_name.length() + 4) > PON_TRX_BUS_FORMAT_SIZE) {
+            cerr << "resultant eeprom file length too long\n";
+            delete []tmp_place_holder;
+            return false;
+        }
+        strcpy(tmp_place_holder, _eeprom_file_path_format.c_str());
+        sprintf(file_name, tmp_place_holder, bus_index[sfp_index], _port_addr, _eeprom_file_name.c_str());
+        memcpy(&_eeprom_read_path[sfp_index][0], file_name, PON_TRX_BUS_FORMAT_SIZE);
+        delete []tmp_place_holder;
+    }
+    cout << "eeprom file to read is: " << _eeprom_read_path[sfp_index].data() << "for sfp " << sfp_index << endl;
+    FILE* fp = fopen(_eeprom_read_path[sfp_index].data(), "rb");
+    if (!fp) {
+        perror("failed to open file");
+        _eeprom_read_path.erase(sfp_index);
+        return false;
+    }
+    _eeprom_data[sfp_index] = data;
+    int val, cnt = 0;
+
+    // Populate EEPROM data
+    while ((val = fgetc(fp)) != EOF && cnt < EEPROM_DATA_READ_SIZE) {
+        _eeprom_data[sfp_index][cnt] = (unsigned char)val;
+        cnt++;
+    }
+    fclose(fp);
+    if (cnt != EEPROM_DATA_READ_SIZE) {
+        cerr << "invalid length of data read:" << cnt << endl;
+        _eeprom_read_path.erase(sfp_index);
+        _eeprom_data.erase(sfp_index);
+        return false;
+    }
+
+    return true;
+}
+
+// decodes the EEPROM data. The sfp_index is the bus index corresponding to the PON Trx
+// returns true if success
+bool PonTrxBase::decode_eeprom_data(int sfp_index) {
+    /*
+    try {
+    */
+
+    trx_data tmp, *store=NULL;
+    tmp.sfp_index = sfp_index;
+
+    // decode vendor name
+    array<unsigned char, EEPROM_VENDOR_NAME_LENGTH> vn{};
+    copy(&_eeprom_data[sfp_index][EEPROM_VENDOR_NAME_START_IDX],
+         &_eeprom_data[sfp_index][EEPROM_VENDOR_NAME_START_IDX] + EEPROM_VENDOR_NAME_LENGTH,
+         &vn[0]);
+    pair<string, bool> res_str = hex_to_ascii_string(vn.data(), vn.max_size());
+    if (!res_str.second) {
+        perror("error decoding vendor name\n");
+        return false;
+    }
+    tmp.vendor_name = res_str.first;
+    cout << "[" << sfp_index << "]" << " vendor name: " << tmp.vendor_name << endl;
+
+
+    // decode vendor oui
+    array<unsigned char, EEPROM_VENDOR_OUI_LENGTH> oui{};
+    copy(&_eeprom_data[sfp_index][EEPROM_VENDOR_OUI_START_IDX],
+         &_eeprom_data[sfp_index][EEPROM_VENDOR_OUI_START_IDX] + EEPROM_VENDOR_OUI_LENGTH,
+         &oui[0]);
+    res_str = hex_to_ascii_string(oui.data(), oui.max_size());
+    if (!res_str.second) {
+        perror("error decoding vendor oui\n");
+        return false;
+    }
+    if (res_str.first.length() == 3) { // vendor_oui length is 3
+        memcpy(tmp.vendor_oui, res_str.first.c_str(), 3);
+    }
+    cout << "[" << sfp_index << "]" << " vendor oui: " << res_str.first << endl;
+
+    // decode vendor part number
+    array<unsigned char, EEPROM_VENDOR_PART_NUMBER_LENGTH> pn{};
+    copy(&_eeprom_data[sfp_index][EEPROM_VENDOR_PART_NUMBER_START_IDX],
+         &_eeprom_data[sfp_index][EEPROM_VENDOR_PART_NUMBER_START_IDX] + EEPROM_VENDOR_PART_NUMBER_LENGTH,
+         &pn[0]);
+    res_str = hex_to_ascii_string(pn.data(), pn.max_size());
+    if (!res_str.second) {
+        perror("error decoding vendor part number\n");
+        return false;
+    }
+    tmp.vendor_part_no = res_str.first;
+    cout << "[" << sfp_index << "]" << " vendor pn: " << tmp.vendor_part_no << endl;
+
+    // decode vendor revision
+    array<unsigned char, EEPROM_VENDOR_REVISION_LENGTH> rev{};
+    copy(&_eeprom_data[sfp_index][EEPROM_VENDOR_REVISION_START_IDX],
+         &_eeprom_data[sfp_index][EEPROM_VENDOR_REVISION_START_IDX] + EEPROM_VENDOR_REVISION_LENGTH,
+         &rev[0]);
+    res_str = hex_to_ascii_string(rev.data(), rev.max_size());
+    if (!res_str.second) {
+        perror("error decoding vendor revision\n");
+        return false;
+    }
+    tmp.vendor_rev = res_str.first;
+    cout << "[" << sfp_index << "]" << " vendor rev: " << tmp.vendor_rev << endl;
+
+    // decode pon wavelength(s)
+    array<unsigned char, EEPROM_DOWNSTREAM_WAVELENGTH_LENGTH> wv{};
+    copy(&_eeprom_data[sfp_index][EEPROM_DOWNSTREAM_WAVELENGTH_START_IDX],
+         &_eeprom_data[sfp_index][EEPROM_DOWNSTREAM_WAVELENGTH_START_IDX] + EEPROM_DOWNSTREAM_WAVELENGTH_LENGTH,
+         &wv[0]);
+    pair<uint32_t, bool> res_uint = hex_to_uinteger(wv.data(), wv.max_size());
+    if (!res_uint.second) {
+        perror("error decoding primary downstream wavelength\n");
+        return false;
+    }
+    tmp.p_data[0].valid = true;
+    tmp.p_data[0].wavelength = res_uint.first * EEPROM_WAVELENGTH_RESOLUTION;
+    tmp.p_data[0].pon_type = get_pon_type_from_wavelength(tmp.p_data[0].wavelength);
+    cout << "[" << sfp_index << "]" << " pon wavelength: " << tmp.p_data[0].wavelength << endl;
+    // TODO: also fill the field pon_type
+
+    // TODO: Let's ignore secondary wavelength decode for now as we do not know how the secondary wavelength is represent on the Combo PON
+    tmp.p_data[1].valid = false;
+
+    store = new(nothrow) trx_data;
+    if (store == NULL) {
+        perror("could not allocate memory for trx_data");
+        return false;
+    }
+
+    // Copy data
+    store->sfp_index = sfp_index;
+    store->vendor_name = tmp.vendor_name;
+    memcpy(store->vendor_oui, tmp.vendor_oui, 3);
+    store->vendor_part_no = tmp.vendor_part_no;
+    store->vendor_rev = tmp.vendor_rev;
+    store->trx_type = tmp.trx_type;
+    for (int i = 0; i < MAX_PONS_PER_TRX; i++) {
+        store->p_data[i].valid = tmp.p_data[i].valid;
+        store->p_data[i].wavelength = tmp.p_data[i].wavelength;
+        store->p_data[i].pon_type = tmp.p_data[i].pon_type;
+    }
+
+    _t_data.insert(store);
+    /*
+    } catch(...) { // FIXME: Put specific exceptions here
+        perror("caught exception\n");
+        return false;
+    }
+    */
+    return true;
+}
+
+bcmolt_pon_type PonTrxBase::get_pon_type_from_wavelength(int wavelength) {
+    bcmolt_pon_type pon_type = BCMOLT_PON_TYPE_UNKNOWN;
+    switch (wavelength) {
+        case GPON_DOWNSTREAM_WAVELENGTH_NM:
+            pon_type = BCMOLT_PON_TYPE_GPON;
+        case XGSPON_DOWNSTREAM_WAVELENGTH_NM:
+            pon_type = BCMOLT_PON_TYPE_XGPON;
+        // Add more in the future as needed.
+    }
+    return pon_type;
+}
+
+bcmolt_pon_type PonTrxBase::get_sfp_mode(int sfp_index) {
+    bcmolt_pon_type pon0_type = BCMOLT_PON_TYPE_UNKNOWN;
+    bcmolt_pon_type pon1_type = BCMOLT_PON_TYPE_UNKNOWN;
+    trx_data *td = NULL;
+    set<trx_data*>::iterator it;
+    // Iterate the trx data set and break if we find a trx data with mathcing sfp index
+    for (it = _t_data.begin(); it != _t_data.end(); it++) {
+        if ((*it)->sfp_index == sfp_index) {
+            td = *it;
+            break;
+        }
+    }
+    if (it == _t_data.end()) {
+        perror("end of iterator, pon type not detected");
+        return pon0_type;
+    }
+    if (td == NULL) {
+        cerr << "trx data is null\n";
+        return pon0_type;
+    }
+
+    // Find the PON type/mode
+    if (td->p_data[0].valid) {
+        pon0_type = td->p_data[0].pon_type;
+        if (pon0_type == BCMOLT_PON_TYPE_UNKNOWN) {
+            return pon0_type;
+        }
+        if (td->p_data[1].valid) {
+            pon1_type = td->p_data[1].pon_type;
+            if (pon1_type == BCMOLT_PON_TYPE_UNKNOWN) {
+                return pon1_type;
+            }
+
+            if  (pon0_type == pon1_type) {
+                return pon0_type;
+            } else {
+                return BCMOLT_PON_TYPE_XGPON_GPON_WDMA; // Combo SFP!
+            }
+        }
+        return pon0_type;
+    }
+
+    return pon0_type;
+}
+
+// Returns the MAC System Mode based on the set of SFP IDs provided.
+// The derived class will most likely need to override this method to provide a
+// different implementation for that particular OLT platform.
+pair<bcmolt_system_mode, bool> PonTrxBase::get_mac_system_mode(int olt_mac_id, set<int> sfp_ids) {
+    bool ret = true;
+    bcmolt_system_mode sm = BCMOLT_SYSTEM_MODE_GPON__16_X;
+
+    return {sm, ret};
+}
+
+// Get trx data for sfp
+trx_data* PonTrxBase::get_trx_data(int sfp_index) {
+    for (auto it : _t_data) {
+        if (it->sfp_index == sfp_index) {
+            cout << "[" << sfp_index << "]" << " found trx data\n";
+            return (it);
+        }
+    }
+    return NULL;
+}
diff --git a/agent/device/device.h b/agent/device/device.h
index 71259ac..a0118a1 100644
--- a/agent/device/device.h
+++ b/agent/device/device.h
@@ -17,8 +17,120 @@
 #ifndef __DEVICE_H__
 #define __DEVICE_H__
 
-#include "vendor.h"
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <algorithm>
+#include <cstdlib>
+#include <sstream>
+#include <new>
+#include <set>
+#include <map>
 
+extern "C"
+{
+#include <bcmolt_api.h>
+#include <bcmolt_api_model_supporting_enums.h>
+}
+
+using namespace std;
+
+/////////////////////////////////////////////////////
+
+// Constant definitions
+
+#define EEPROM_DATA_READ_SIZE 256 //bytes
+#define EEPROM_READ_PATH_SIZE 200
+#define PON_TRX_BUS_FORMAT_SIZE 64
+#define MAX_PONS_PER_TRX 2
+
+#define GPON_DOWNSTREAM_WAVELENGTH_NM 1490 // ITU-T G984.2 PMD specification
+#define XGSPON_DOWNSTREAM_WAVELENGTH_NM 1577 // ITU-T G9807.1 PMD specification
+
+
+/////////////////////////////////////////////////////
+
+// Structure definitions
+
+struct pon_data {
+    bool valid; // set to true if the below fields are set after reading eeprom data
+    uint32_t wavelength;
+    bcmolt_pon_type pon_type;
+    // add more fields as needed...
+};
+
+struct trx_data {
+    int sfp_index;
+    string vendor_name;
+    uint8_t vendor_oui[3];
+    string vendor_part_no;
+    string vendor_rev;
+    bcmolt_pon_type trx_type;
+    pon_data p_data[MAX_PONS_PER_TRX];
+};
+
+/////////////////////////////////////////////////////
+
+// Class Definitions
+
+// PonTrx: This class defines the member functions and attributes of the Pon Transceiver object
+class PonTrxBase {
+    public:
+
+        // Location of I2C file systems is pretty standard for Linux systems, and it is /sys/bus/i2c/devices
+        // Check here https://www.kernel.org/doc/html/latest/i2c/i2c-sysfs.html#location-of-i2c-sysfs for documentation on Linux I2C
+        // The OLTs we work with are based on Linux, however if it is something different this can be overridden.
+        PonTrxBase(string eeprom_file_name="sfp_eeprom",
+                   int port_addr=50,
+                   string eeprom_file_format_name="/sys/bus/i2c/devices/%d-00%d/%s",
+                   string sfp_presence_command="/lib/platform-config/current/onl/bin/onlpdump -p");
+
+        // Reads, updates and returns the _sfp_presence_array from the OLT device
+        const set<int> read_sfp_presence_data();
+
+        // Returns the _sfp_presence_array
+        const set<int> get_sfp_presence_data();
+
+        // Reads the EEPROM data. The sfp_index is the bus index corresponding to the PON Trx
+        // returns true if success
+        bool read_eeprom_data_for_sfp(int sfp_index);
+
+        // Decodes the EEPROM data. The sfp_index is the bus index corresponding to the PON Trx
+        // returns true if success
+        bool decode_eeprom_data(int sfp_index);
+
+        // Get Trx Type based on SFP ID provided
+        bcmolt_pon_type get_sfp_mode(int sfp_index);
+
+        // Get PON Type based on optical wavelength specified
+        bcmolt_pon_type get_pon_type_from_wavelength(int wavelength);
+
+        // Get MAC System mode based on the olt mac id and the set of SFP IDs provided
+        pair<bcmolt_system_mode, bool> get_mac_system_mode(int, set<int>);
+
+        // Get Trx data
+        trx_data* get_trx_data(int sfp_index);
+
+        ~PonTrxBase();
+
+    protected:
+        set<int> _sfp_presence_data;
+        set<trx_data*> _t_data;
+        map<int, array<char, EEPROM_READ_PATH_SIZE>> _eeprom_read_path;
+        map<int, array<unsigned char, EEPROM_DATA_READ_SIZE>> _eeprom_data;
+        // Location of I2C file systems is pretty standard for Linux systems, and it is /sys/bus/i2c/devices
+        // Check here https://www.kernel.org/doc/html/latest/i2c/i2c-sysfs.html#location-of-i2c-sysfs for documentation on Linux I2C
+        // The OLTs we work with are based on Linux, however if it is something different this can be overridden during objection creation.
+        string _eeprom_file_path_format = "/sys/bus/i2c/devices/%d-00%d/%s";
+        string _eeprom_file_name;
+        string _sfp_presence_command = "/lib/platform-config/current/onl/bin/onlpdump -p";
+        int _port_addr;
+
+};
+
+/////////////////////////////////////////////////////
+
+// Extern definitions
 extern void vendor_init();
 
 #endif
diff --git a/agent/device/generic/vendor.h b/agent/device/generic/vendor.h
index 8d2a5a0..987d927 100644
--- a/agent/device/generic/vendor.h
+++ b/agent/device/generic/vendor.h
@@ -16,6 +16,7 @@
 
 #ifndef __VENDOR_H__
 #define __VENDOR_H__
+#include "device.h"
 #define VENDOR_ID "generic"
 #define MODEL_ID  "generic"
 
@@ -38,4 +39,52 @@
 
 #define MAC_DEVICE_ACTIVATION_DELAY 200000 // in microseconds
 
+#define DEFAULT_MAC_SYSTEM_MODE BCMOLT_SYSTEM_MODE_GPON__16_X
+//#define DEFAULT_MAC_SYSTEM_MODE BCMOLT_SYSTEM_MODE_XGS__2_X
+#define DEFAULT_PON_MODE BCMOLT_PON_TYPE_GPON
+// #define DEFAULT_PON_MODE BCMOLT_PON_TYPE_XGPON
+
+
+#define TOTAL_PON_TRX_PORTS 16 // total PON transceiver ports
+#define TOTAL_PON_PORTS 16 // total PON ports (we could have up to 2 PON ports on the OLT MAC mapped to the external PON Trx)
+const int trx_port_to_pon_port_map[TOTAL_PON_TRX_PORTS][TOTAL_PON_PORTS/TOTAL_PON_TRX_PORTS]={{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},
+{11},{12},{13},{14},{15}};
+#define PONS_PER_TRX (TOTAL_PON_TRX_PORTS/TOTAL_PON_PORTS) // if there are more than one OLT MAC device,
+                                                           // they all have to be of the same type for this to work.
+
+const int bus_index[TOTAL_PON_TRX_PORTS] = {
+   41,  42,  56,  55,  43,  44,  54,  53,
+   45,  46,  52,  51,  47,  48,  50,  49
+};
+
+
+#define PORT_ADDRESS 50
+
+#define NAME_EEPROM "sfp_eeprom"
+
+#define EEPROM_VENDOR_NAME_START_IDX 148
+#define EEPROM_VENDOR_NAME_LENGTH 16
+
+#define EEPROM_VENDOR_OUI_START_IDX 165
+#define EEPROM_VENDOR_OUI_LENGTH 3
+
+#define EEPROM_VENDOR_PART_NUMBER_START_IDX 168
+#define EEPROM_VENDOR_PART_NUMBER_LENGTH 16
+
+#define EEPROM_VENDOR_REVISION_START_IDX 184
+#define EEPROM_VENDOR_REVISION_LENGTH 2
+
+#define EEPROM_DOWNSTREAM_WAVELENGTH_START_IDX 186
+#define EEPROM_DOWNSTREAM_WAVELENGTH_LENGTH 2
+#define EEPROM_WAVELENGTH_RESOLUTION 0.05
+
+// Define valid values below in case of Combo PON Trx is supported
+// #define EEPROM_DOWNSTREAM_SECONDARY_WAVELENGTH_START_IDX 120
+// #define EEPROM_DOWNSTREAM_SECONDARY_WAVELENGTH_LENGTH 2
+
+class PonTrx: public PonTrxBase {
+   // override the base member functions if you need a different implementation
+};
+
+
 #endif
diff --git a/agent/device/rlt-1600g-w/vendor.h b/agent/device/rlt-1600g-w/vendor.h
index 2a62bcb..2940b91 100644
--- a/agent/device/rlt-1600g-w/vendor.h
+++ b/agent/device/rlt-1600g-w/vendor.h
@@ -16,6 +16,8 @@
 
 #ifndef __VENDOR_H__
 #define __VENDOR_H__
+#include "device.h"
+
 #define VENDOR_ID "Radisys"
 #define RLT1600GW
 #define MODEL_ID  "rlt-1600g-w"
@@ -23,6 +25,12 @@
 #define ONU_BIT_TRANSMISSION_DELAY 0.8038585/1000 /* unit: ns to us */
 #define MINIMUM_ONU_RESPONSE_RANGING_TIME 198075 /* hardcore: this is ranging time for the shortest distance, typically 35us */
 
+#define DEFAULT_MAC_SYSTEM_MODE BCMOLT_SYSTEM_MODE_GPON__16_X
+//#define DEFAULT_MAC_SYSTEM_MODE BCMOLT_SYSTEM_MODE_XGS__2_X
+#define DEFAULT_PON_MODE BCMOLT_PON_TYPE_GPON
+// #define DEFAULT_PON_MODE BCMOLT_PON_TYPE_XGPON
+
+
 // DeviceInfo definitions
 
 #define ONU_ID_START 1
@@ -44,4 +52,48 @@
 
 #define MAC_DEVICE_ACTIVATION_DELAY 200000 // in microseconds
 
+#define TOTAL_PON_TRX_PORTS 16 // total PON transceiver ports
+#define TOTAL_PON_PORTS 16 // total PON ports (we could have up to 2 PON ports on the OLT MAC mapped to the external PON Trx)
+const int trx_port_to_pon_port_map[TOTAL_PON_TRX_PORTS][TOTAL_PON_PORTS/TOTAL_PON_TRX_PORTS]={{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},
+{11},{12},{13},{14},{15}};
+#define PONS_PER_TRX (TOTAL_PON_TRX_PORTS/TOTAL_PON_PORTS) // if there are more than one OLT MAC device,
+                                                           // they all have to be of the same type for this to work.
+
+// FIXME: These may not be correct for this platform
+const int bus_index[TOTAL_PON_TRX_PORTS] = {
+   41,  42,  56,  55,  43,  44,  54,  53,
+   45,  46,  52,  51,  47,  48,  50,  49
+};
+
+// FIXME: Check the correctness of the below constants for this platform
+
+#define PORT_ADDRESS 50
+
+#define NAME_EEPROM "eeprom"
+
+#define EEPROM_VENDOR_NAME_START_IDX 20
+#define EEPROM_VENDOR_NAME_LENGTH 16
+
+#define EEPROM_VENDOR_OUI_START_IDX 37
+#define EEPROM_VENDOR_OUI_LENGTH 3
+
+#define EEPROM_VENDOR_PART_NUMBER_START_IDX 40
+#define EEPROM_VENDOR_PART_NUMBER_LENGTH 16
+
+#define EEPROM_VENDOR_REVISION_START_IDX 56
+#define EEPROM_VENDOR_REVISION_LENGTH 4
+
+#define EEPROM_DOWNSTREAM_WAVELENGTH_START_IDX 60
+#define EEPROM_DOWNSTREAM_WAVELENGTH_LENGTH 2
+#define EEPROM_WAVELENGTH_RESOLUTION 1
+
+// Define valid values below in case of Combo PON Trx is supported
+// #define EEPROM_DOWNSTREAM_SECONDARY_WAVELENGTH_START_IDX 120
+// #define EEPROM_DOWNSTREAM_SECONDARY_WAVELENGTH_LENGTH 2
+
+class PonTrx: public PonTrxBase {
+   // override the base member functions if you need a different implementation
+
+};
+
 #endif
diff --git a/agent/device/rlt-1600x-w/vendor.h b/agent/device/rlt-1600x-w/vendor.h
index ec6b5c6..5c8425d 100644
--- a/agent/device/rlt-1600x-w/vendor.h
+++ b/agent/device/rlt-1600x-w/vendor.h
@@ -16,6 +16,8 @@
 
 #ifndef __VENDOR_H__
 #define __VENDOR_H__
+#include "device.h"
+
 #define VENDOR_ID "Radisys"
 #define RLT1600XW
 #define MODEL_ID  "rlt-1600x-w"
@@ -44,4 +46,54 @@
 
 #define MAC_DEVICE_ACTIVATION_DELAY 200000 // in microseconds
 
+#define DEFAULT_MAC_SYSTEM_MODE BCMOLT_SYSTEM_MODE_GPON__16_X
+//#define DEFAULT_MAC_SYSTEM_MODE BCMOLT_SYSTEM_MODE_XGS__2_X
+#define DEFAULT_PON_MODE BCMOLT_PON_TYPE_GPON
+// #define DEFAULT_PON_MODE BCMOLT_PON_TYPE_XGPON
+
+
+#define TOTAL_PON_TRX_PORTS 16 // total PON transceiver ports
+#define TOTAL_PON_PORTS 16 // total PON ports (we could have up to 2 PON ports on the OLT MAC mapped to the external PON Trx)
+const int trx_port_to_pon_port_map[TOTAL_PON_TRX_PORTS][TOTAL_PON_PORTS/TOTAL_PON_TRX_PORTS]={{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},
+{11},{12},{13},{14},{15}};
+#define PONS_PER_TRX (TOTAL_PON_TRX_PORTS/TOTAL_PON_PORTS) // if there are more than one OLT MAC device,
+                                                           // they all have to be of the same type for this to work.
+
+// FIXME: These may not be correct for this platform
+const int bus_index[TOTAL_PON_TRX_PORTS] = {
+   41,  42,  56,  55,  43,  44,  54,  53,
+   45,  46,  52,  51,  47,  48,  50,  49
+};
+
+// FIXME: Check the correctness of the below constants for this platform
+
+#define PORT_ADDRESS 50
+
+#define NAME_EEPROM "eeprom"
+
+#define EEPROM_VENDOR_NAME_START_IDX 20
+#define EEPROM_VENDOR_NAME_LENGTH 16
+
+#define EEPROM_VENDOR_OUI_START_IDX 37
+#define EEPROM_VENDOR_OUI_LENGTH 3
+
+#define EEPROM_VENDOR_PART_NUMBER_START_IDX 40
+#define EEPROM_VENDOR_PART_NUMBER_LENGTH 16
+
+#define EEPROM_VENDOR_REVISION_START_IDX 56
+#define EEPROM_VENDOR_REVISION_LENGTH 4
+
+#define EEPROM_DOWNSTREAM_WAVELENGTH_START_IDX 60
+#define EEPROM_DOWNSTREAM_WAVELENGTH_LENGTH 2
+#define EEPROM_WAVELENGTH_RESOLUTION 1
+
+// Define valid values below in case of Combo PON Trx is supported
+// #define EEPROM_DOWNSTREAM_SECONDARY_WAVELENGTH_START_IDX 120
+// #define EEPROM_DOWNSTREAM_SECONDARY_WAVELENGTH_LENGTH 2
+
+class PonTrx: public PonTrxBase {
+   // override the base member functions if you need a different implementation
+
+};
+
 #endif
diff --git a/agent/device/rlt-3200g-w/vendor.h b/agent/device/rlt-3200g-w/vendor.h
index b55cc02..df21c32 100644
--- a/agent/device/rlt-3200g-w/vendor.h
+++ b/agent/device/rlt-3200g-w/vendor.h
@@ -16,6 +16,8 @@
 
 #ifndef __VENDOR_H__
 #define __VENDOR_H__
+#include "device.h"
+
 #define VENDOR_ID "Radisys"
 #define RLT3200GW
 #define MODEL_ID  "rlt-3200g-w"
@@ -44,4 +46,56 @@
 
 #define MAC_DEVICE_ACTIVATION_DELAY 200000 // in microseconds
 
+#define DEFAULT_MAC_SYSTEM_MODE BCMOLT_SYSTEM_MODE_GPON__16_X
+//#define DEFAULT_MAC_SYSTEM_MODE BCMOLT_SYSTEM_MODE_XGS__2_X
+#define DEFAULT_PON_MODE BCMOLT_PON_TYPE_GPON
+// #define DEFAULT_PON_MODE BCMOLT_PON_TYPE_XGPON
+
+
+#define TOTAL_PON_TRX_PORTS 32 // total PON transceiver ports
+#define TOTAL_PON_PORTS 32 // total PON ports (we could have up to 2 PON ports on the OLT MAC mapped to the external PON Trx)
+const int trx_port_to_pon_port_map[TOTAL_PON_TRX_PORTS][TOTAL_PON_PORTS/TOTAL_PON_TRX_PORTS]={{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},
+{11},{12},{13},{14},{15},{16},{17},{18},{19},{20},{21},{22},{23},{24},{25},{26},{27},{28},{29},{30},{31}};
+#define PONS_PER_TRX (TOTAL_PON_TRX_PORTS/TOTAL_PON_PORTS) // if there are more than one OLT MAC device,
+                                                           // they all have to be of the same type for this to work.
+
+// FIXME: These may not be correct for this platform
+const int bus_index[TOTAL_PON_TRX_PORTS] = {
+   41,  42,  56,  55,  43,  44,  54,  53,
+   45,  46,  52,  51,  47,  48,  50,  49,
+   57,  58,  72,  71,  59,  60,  70,  69,
+   61,  62,  68,  67,  63,  64,  66,  65
+};
+
+// FIXME: Check the correctness of the below constants for this platform
+
+#define PORT_ADDRESS 50
+
+#define NAME_EEPROM "eeprom"
+
+#define EEPROM_VENDOR_NAME_START_IDX 20
+#define EEPROM_VENDOR_NAME_LENGTH 16
+
+#define EEPROM_VENDOR_OUI_START_IDX 37
+#define EEPROM_VENDOR_OUI_LENGTH 3
+
+#define EEPROM_VENDOR_PART_NUMBER_START_IDX 40
+#define EEPROM_VENDOR_PART_NUMBER_LENGTH 16
+
+#define EEPROM_VENDOR_REVISION_START_IDX 56
+#define EEPROM_VENDOR_REVISION_LENGTH 4
+
+#define EEPROM_DOWNSTREAM_WAVELENGTH_START_IDX 60
+#define EEPROM_DOWNSTREAM_WAVELENGTH_LENGTH 2
+#define EEPROM_WAVELENGTH_RESOLUTION 1
+
+// Define valid values below in case of Combo PON Trx is supported
+// #define EEPROM_DOWNSTREAM_SECONDARY_WAVELENGTH_START_IDX 120
+// #define EEPROM_DOWNSTREAM_SECONDARY_WAVELENGTH_LENGTH 2
+
+class PonTrx: public PonTrxBase {
+   // override the base member functions if you need a different implementation
+
+};
+
 #endif
diff --git a/agent/device/sda3016ss/vendor.cc b/agent/device/sda3016ss/vendor.cc
index 86836ff..75542bf 100755
--- a/agent/device/sda3016ss/vendor.cc
+++ b/agent/device/sda3016ss/vendor.cc
@@ -19,3 +19,13 @@
 void vendor_init()
 {
 }
+
+// Returns the MAC System Mode based on the set of SFP IDs provided.
+// The derived class will most likely need to override this method to provide a
+// different implementation for that particular OLT platform.
+pair<bcmolt_system_mode, bool> PonTrx::get_mac_system_mode(int olt_mac_id, set<int> sfp_ids) {
+    bool ret = true;
+    bcmolt_system_mode sm = BCMOLT_SYSTEM_MODE_XGS__8_X_GPON__8_X_WDMA;
+
+    return {sm, ret};
+}
diff --git a/agent/device/sda3016ss/vendor.h b/agent/device/sda3016ss/vendor.h
index d904e9b..b30960b 100755
--- a/agent/device/sda3016ss/vendor.h
+++ b/agent/device/sda3016ss/vendor.h
@@ -23,6 +23,8 @@
 #define MAX_SUPPORTED_SWITCH_PORT 16
 #define ONU_BIT_TRANSMISSION_DELAY 0.1004823/1000 /* unit: ns to us */
 #define MINIMUM_ONU_RESPONSE_RANGING_TIME 1572135 /* hardcore: this is ranging time for the shortest distance, typically 35us */
+#define DEFAULT_MAC_SYSTEM_MODE BCMOLT_SYSTEM_MODE_XGS__8_X_GPON__8_X_WDMA
+#define DEFAULT_PON_MODE BCMOLT_PON_TYPE_XGPON
 
 // DeviceInfo definitions
 
@@ -44,5 +46,60 @@
 #define INVALID_FLOW_ID 0
 
 #define MAC_DEVICE_ACTIVATION_DELAY 200000 // in microseconds
+///////////////////////////////////////////////////////
+// Constants relevant for decoding PON Trx EEPROM Data
+
+// Uncomment below line when you need dynamic transceiver detection support
+// #define DYNAMIC_PON_TRX_SUPPORT
+
+
+#define TOTAL_PON_TRX_PORTS 16 // total PON transceiver ports
+#define TOTAL_PON_PORTS 32 // total PON ports (we could have up to 2 PON ports on the OLT MAC mapped to the external PON Trx)
+const int trx_port_to_pon_port_map[TOTAL_PON_TRX_PORTS][TOTAL_PON_PORTS/TOTAL_PON_TRX_PORTS]={{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},
+{11},{12},{13},{14},{15}};
+#define PONS_PER_TRX (TOTAL_PON_TRX_PORTS/TOTAL_PON_PORTS) // if there are more than one OLT MAC device,
+                                                           // they all have to be of the same type for this to work.
+
+const int bus_index[TOTAL_PON_TRX_PORTS] = {
+   47,  48,  37,  38,  35,  36,  33,  34,
+   39,  40,  41,  42,  43,  44,  45,  46
+};
+
+
+// FIXME: Check the correctness of the below constants for this platform
+// This is Combo PON OLT - so not all the values below are accurate.
+
+#define PORT_ADDRESS 50
+
+#define NAME_EEPROM "sfp_eeprom"
+
+#define EEPROM_VENDOR_NAME_START_IDX 148
+#define EEPROM_VENDOR_NAME_LENGTH 16
+
+#define EEPROM_VENDOR_OUI_START_IDX 165
+#define EEPROM_VENDOR_OUI_LENGTH 3
+
+#define EEPROM_VENDOR_PART_NUMBER_START_IDX 168
+#define EEPROM_VENDOR_PART_NUMBER_LENGTH 16
+
+#define EEPROM_VENDOR_REVISION_START_IDX 184
+#define EEPROM_VENDOR_REVISION_LENGTH 2
+
+#define EEPROM_DOWNSTREAM_WAVELENGTH_START_IDX 186
+#define EEPROM_DOWNSTREAM_WAVELENGTH_LENGTH 2
+#define EEPROM_WAVELENGTH_RESOLUTION 0.05
+
+// Define valid values below in case of Combo PON Trx is supported
+// #define EEPROM_DOWNSTREAM_SECONDARY_WAVELENGTH_START_IDX 120
+// #define EEPROM_DOWNSTREAM_SECONDARY_WAVELENGTH_LENGTH 2
+
+
+class PonTrx: public PonTrxBase {
+   // override the base member functions if you need a different implementation
+   public:
+      // Get MAC System mode based on the olt mac id and the set of SFP IDs provided
+      pair<bcmolt_system_mode, bool> get_mac_system_mode(int, set<int>);
+
+};
 
 #endif
diff --git a/agent/device/sim/vendor.cc b/agent/device/sim/vendor.cc
index 86836ff..7e68606 100644
--- a/agent/device/sim/vendor.cc
+++ b/agent/device/sim/vendor.cc
@@ -18,4 +18,4 @@
 
 void vendor_init()
 {
-}
+}
\ No newline at end of file
diff --git a/agent/device/sim/vendor.h b/agent/device/sim/vendor.h
index 1dbc9cd..3fab2a6 100644
--- a/agent/device/sim/vendor.h
+++ b/agent/device/sim/vendor.h
@@ -16,6 +16,9 @@
 
 #ifndef __VENDOR_H__
 #define __VENDOR_H__
+
+#include "device.h"
+
 #define VENDOR_ID "ONF"
 #define SIM
 #define MODEL_ID  "sim"
@@ -44,4 +47,63 @@
 
 #define MAC_DEVICE_ACTIVATION_DELAY 200000 // in microseconds
 
+#define DEFAULT_MAC_SYSTEM_MODE BCMOLT_SYSTEM_MODE_GPON__16_X
+//#define DEFAULT_MAC_SYSTEM_MODE BCMOLT_SYSTEM_MODE_XGS__2_X
+#define DEFAULT_PON_MODE BCMOLT_PON_TYPE_GPON
+// #define DEFAULT_PON_MODE BCMOLT_PON_TYPE_XGPON
+
+
+///////////////////////////////////////////////////////
+// Constants relevant for decoding PON Trx EEPROM Data
+
+#define DYNAMIC_PON_TRX_SUPPORT
+
+#define TOTAL_PON_TRX_PORTS 16 // total PON transceiver ports
+#define TOTAL_PON_PORTS 16 // total PON ports (we could have up to 2 PON ports on the OLT MAC mapped to the external PON Trx)
+const int trx_port_to_pon_port_map[TOTAL_PON_TRX_PORTS][TOTAL_PON_PORTS/TOTAL_PON_TRX_PORTS]={{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},
+{11},{12},{13},{14},{15}};
+#define PONS_PER_TRX (TOTAL_PON_TRX_PORTS/TOTAL_PON_PORTS) // if there are more than one OLT MAC device,
+                                                           // they all have to be of the same type for this to work.
+
+const int bus_index[TOTAL_PON_TRX_PORTS] = {
+   47,  48,  37,  38,  35,  36,  33,  34,
+   39,  40,  41,  42,  43,  44,  45,  46
+};
+
+#define PORT_ADDRESS 50
+
+#define NAME_EEPROM "sfp_eeprom"
+
+#define EEPROM_VENDOR_NAME_START_IDX 148
+#define EEPROM_VENDOR_NAME_LENGTH 16
+
+#define EEPROM_VENDOR_OUI_START_IDX 165
+#define EEPROM_VENDOR_OUI_LENGTH 3
+
+#define EEPROM_VENDOR_PART_NUMBER_START_IDX 168
+#define EEPROM_VENDOR_PART_NUMBER_LENGTH 16
+
+#define EEPROM_VENDOR_REVISION_START_IDX 184
+#define EEPROM_VENDOR_REVISION_LENGTH 2
+
+#define EEPROM_DOWNSTREAM_WAVELENGTH_START_IDX 186
+#define EEPROM_DOWNSTREAM_WAVELENGTH_LENGTH 2
+#define EEPROM_WAVELENGTH_RESOLUTION 0.05
+
+// Define valid values below in case of Combo PON Trx is supported
+// #define EEPROM_DOWNSTREAM_SECONDARY_WAVELENGTH_START_IDX 120
+// #define EEPROM_DOWNSTREAM_SECONDARY_WAVELENGTH_LENGTH 2
+
+///////////////////////////////////////////////////////
+
+
+class PonTrx: public PonTrxBase {
+   public:
+      // override the base member functions if you need a different implementation
+      PonTrx(): PonTrxBase("sfp_eeprom", 50, "%d-00%d/%s") {
+
+      }
+
+};
+
 #endif
diff --git a/agent/src/core_api_handler.cc b/agent/src/core_api_handler.cc
index a833077..5b7bb74 100644
--- a/agent/src/core_api_handler.cc
+++ b/agent/src/core_api_handler.cc
@@ -403,12 +403,12 @@
                 BCMOLT_CFG_INIT(&dev_cfg, device, dev_key);
                 BCMOLT_MSG_FIELD_GET(&dev_cfg, system_mode);
 
-		/* FIXME: Single Phoenix BAL patch is prepared for all three variants of Radisys OLT
-		 * in which BCM_MAX_DEVS_PER_LINE_CARD macro need to be redefined as 1 incase of
-		 * "rlt-1600g-w" and "rlt-1600x-w", till then this workaround is required.*/
+		        /* FIXME: Single Phoenix BAL patch is prepared for all three variants of Radisys OLT
+		        * in which BCM_MAX_DEVS_PER_LINE_CARD macro need to be redefined as 1 incase of
+		        * "rlt-1600g-w" and "rlt-1600x-w", till then this workaround is required.*/
                 if (dev == 1 && (MODEL_ID == "rlt-1600g-w" || MODEL_ID == "rlt-1600x-w")) {
-		    continue;
-		}
+                    continue;
+                }
 
                 err = bcmolt_cfg_get(dev_id, &dev_cfg.hdr);
                 if (err == BCM_ERR_NOT_CONNECTED) {
@@ -416,18 +416,27 @@
                     bcmolt_device_connect oper;
                     BCMOLT_OPER_INIT(&oper, device, connect, key);
 
-		    /* BAL saves current state into dram_tune soc file and when dev_mgmt_daemon restarts
-		     * it retains config from soc file. If openolt agent try to connect device without
-		     * device reset device initialization fails hence doing device reset here. */
+		            /* BAL saves current state into dram_tune soc file and when dev_mgmt_daemon restarts
+		            * it retains config from soc file. If openolt agent try to connect device without
+		            * device reset device initialization fails hence doing device reset here. */
                     reset_pon_device(dev);
-
+                    bcmolt_system_mode sm;
+                    #ifdef DYNAMIC_PON_TRX_SUPPORT
+                    auto sm_res = ponTrx.get_mac_system_mode(dev, ponTrx.get_sfp_presence_data());
+                    if (!sm_res.second) {
+                        OPENOLT_LOG(ERROR, openolt_log_id, "could not read mac system mode. dev_id = %d\n", dev);
+                        continue;
+                    }
+                    sm = sm_res.first;
+                    #else
+                    sm = DEFAULT_MAC_SYSTEM_MODE;
+                    #endif
+                    BCMOLT_MSG_FIELD_SET (&oper, system_mode, sm);
                     if (MODEL_ID == "asfvolt16") {
                         BCMOLT_MSG_FIELD_SET(&oper, inni_config.mode, BCMOLT_INNI_MODE_ALL_10_G_XFI);
-                        BCMOLT_MSG_FIELD_SET (&oper, system_mode, BCMOLT_SYSTEM_MODE_XGS__2_X);
                     } else if (MODEL_ID == "asgvolt64") {
                         BCMOLT_MSG_FIELD_SET(&oper, inni_config.mode, BCMOLT_INNI_MODE_ALL_10_G_XFI);
                         BCMOLT_MSG_FIELD_SET(&oper, inni_config.mux, BCMOLT_INNI_MUX_FOUR_TO_ONE);
-                        BCMOLT_MSG_FIELD_SET (&oper, system_mode, BCMOLT_SYSTEM_MODE_GPON__16_X);
                     } else if (MODEL_ID == "rlt-3200g-w" || MODEL_ID == "rlt-1600g-w") {
                         BCMOLT_MSG_FIELD_SET(&oper, inni_config.mux, BCMOLT_INNI_MUX_NONE);
                         if(dev == 1) {
@@ -435,23 +444,14 @@
                         }
                         BCMOLT_MSG_FIELD_SET (&oper, ras_ddr_mode, BCMOLT_RAS_DDR_USAGE_MODE_TWO_DDRS);
                         BCMOLT_MSG_FIELD_SET(&oper, inni_config.mode, BCMOLT_INNI_MODE_ALL_10_G_XFI);
-                        BCMOLT_MSG_FIELD_SET (&oper, system_mode, BCMOLT_SYSTEM_MODE_GPON__16_X);
                     } else if (MODEL_ID == "rlt-1600x-w") {
                         BCMOLT_MSG_FIELD_SET(&oper, inni_config.mux, BCMOLT_INNI_MUX_NONE);
                         BCMOLT_MSG_FIELD_SET (&oper, ras_ddr_mode, BCMOLT_RAS_DDR_USAGE_MODE_TWO_DDRS);
                         BCMOLT_MSG_FIELD_SET(&oper, inni_config.mode, BCMOLT_INNI_MODE_ALL_10_G_XFI);
-                        /* By default setting device mode to GPON for rlt 1600x.
-                           In future device mode can be configured to XGSPON || GPON by reading
-                           device mode configuration from a static configuration file*/
-                        BCMOLT_MSG_FIELD_SET (&oper, system_mode, BCMOLT_SYSTEM_MODE_GPON__16_X);
                     } else if (MODEL_ID == "sda3016ss") {
                         BCMOLT_MSG_FIELD_SET(&oper, inni_config.mode, BCMOLT_INNI_MODE_ALL_12_P_5_G);
                         BCMOLT_MSG_FIELD_SET(&oper, inni_config.mux, BCMOLT_INNI_MUX_TWO_TO_ONE);
 						BCMOLT_MSG_FIELD_SET (&oper, ras_ddr_mode, BCMOLT_RAS_DDR_USAGE_MODE_TWO_DDRS);
-                        BCMOLT_MSG_FIELD_SET (&oper, system_mode, BCMOLT_SYSTEM_MODE_XGS__8_X_GPON__8_X_WDMA);
-                        /* By default setting device mode to XGSPON/GPON combo for sda3016ss.
-                           And it can also be configured to Any-PON (XGSPON || GPON) */
-
                     }
                     err = bcmolt_oper_submit(dev_id, &oper.hdr);
                     if (err) {
@@ -942,6 +942,9 @@
         BCMOLT_MSG_FIELD_SET(&interface_obj, itu.gpon.power_level.mode, BCMOLT_PON_POWER_LEVEL_MODE_DEFAULT);
     }
 
+    // TODO: Currently the PON Type is set automatically when the MAC System Mode is set. But it could be explicitely set here again.
+    // The data for the PON type is availabe in the PonTrx object (check trx_data)
+
     //Enable AES Encryption
     BCMOLT_MSG_FIELD_SET(&interface_obj, itu.onu_activation.key_exchange, BCMOLT_CONTROL_STATE_ENABLE);
     BCMOLT_MSG_FIELD_SET(&interface_obj, itu.onu_activation.authentication, BCMOLT_CONTROL_STATE_ENABLE);
diff --git a/agent/src/core_data.cc b/agent/src/core_data.cc
index bd83e81..da9c733 100644
--- a/agent/src/core_data.cc
+++ b/agent/src/core_data.cc
@@ -231,3 +231,6 @@
 // Read Rx optical power
 std::map<onu_rssi_compltd_key, Queue<onu_rssi_complete_result>*> onu_rssi_compltd_map;
 bcmos_fastlock onu_rssi_wait_lock;
+
+// PonTrx class object
+PonTrx ponTrx;
diff --git a/agent/src/core_utils.cc b/agent/src/core_utils.cc
index af3f7ad..dafc68c 100644
--- a/agent/src/core_utils.cc
+++ b/agent/src/core_utils.cc
@@ -1979,3 +1979,27 @@
         return false;
     }
 }
+
+pair<string, bool> hex_to_ascii_string(unsigned char* ptr, int length) {
+    // initialize the ASCII code string as empty.
+    string ascii = "";
+    for (size_t i = 0; i < length; i ++)
+    {
+        string part = string(1,ptr[i]);
+        ascii += part;
+    }
+    return {ascii, true};
+}
+
+pair<uint32_t, bool> hex_to_uinteger(unsigned char *ptr, int length) {
+    if (length > 8) {
+        perror("invalid length of bytes for conversion to uint\n");
+        return {0, false};
+    }
+    uint32_t res = 0;
+    for (int i = 0; i < length; i++)
+    {
+        res = uint32_t(ptr[i]) * pow(2, (length - 1 - i) * 8) + res;
+    }
+    return {res, true};
+}
diff --git a/agent/src/core_utils.h b/agent/src/core_utils.h
index 344ce5e..1813a37 100644
--- a/agent/src/core_utils.h
+++ b/agent/src/core_utils.h
@@ -127,4 +127,6 @@
 bool save_to_txt_file(const std::string& file_name, const std::string& content);
 bcmos_errno get_gem_obj_state(bcmolt_interface pon_ni, bcmolt_gem_port_id id, bcmolt_activation_state *state);
 bcmos_errno get_alloc_obj_state(bcmolt_interface pon_ni, bcmolt_alloc_id id, bcmolt_activation_state *state);
+pair<string, bool> hex_to_ascii_string(unsigned char* ptr, int length);
+pair<uint32_t, bool> hex_to_uinteger(unsigned char *ptr, int length);
 #endif // OPENOLT_CORE_UTILS_H_
diff --git a/agent/test/41-0050/NOTE.md b/agent/test/41-0050/NOTE.md
new file mode 100644
index 0000000..5d1add7
--- /dev/null
+++ b/agent/test/41-0050/NOTE.md
@@ -0,0 +1,2 @@
+# Binary file format
+Confirms to SFF-8472 specification for SFP+ modules.
diff --git a/agent/test/41-0050/eeprom b/agent/test/41-0050/eeprom
new file mode 100644
index 0000000..7c461db
--- /dev/null
+++ b/agent/test/41-0050/eeprom
Binary files differ
diff --git a/agent/test/47-0050/NOTE.md b/agent/test/47-0050/NOTE.md
new file mode 100644
index 0000000..cd12da8
--- /dev/null
+++ b/agent/test/47-0050/NOTE.md
@@ -0,0 +1,2 @@
+# Binary file format
+Confirms to SFF-8436 specification for XFP modules.
diff --git a/agent/test/47-0050/sfp_eeprom b/agent/test/47-0050/sfp_eeprom
new file mode 100644
index 0000000..d69a119
--- /dev/null
+++ b/agent/test/47-0050/sfp_eeprom
Binary files differ
diff --git a/agent/test/Makefile b/agent/test/Makefile
index 5897ad3..94e0829 100644
--- a/agent/test/Makefile
+++ b/agent/test/Makefile
@@ -122,7 +122,7 @@
 	make -C $(OPENOLT_PROTOS_DIR) clean
 
 # openolt
-OPENOLT_SRCS = $(wildcard ../src/*.cc) ../common/server.cc $(wildcard ../device/$(OPENOLTDEVICE)/*.cc) src/bal_stub.cc
+OPENOLT_SRCS = $(wildcard ../src/*.cc) ../common/server.cc ../device/device.cc $(wildcard ../device/$(OPENOLTDEVICE)/*.cc) src/bal_stub.cc
 OPENOLT_OBJS = $(OPENOLT_SRCS:.cc=.o)
 $(OPENOLT_LIB): $(OPENOLT_OBJS)
 	mkdir -p lib
@@ -134,6 +134,8 @@
 	$(CXX) $(CXXFLAGS) $(CXXFLAGSDEVICE) -I../common -I./inc -I../src -c $< -o $@
 src/%.o: src/%.cc
 	$(CXX) $(CXXFLAGS) $(CXXFLAGSDEVICE) -I../common -I./inc -I../src -c $< -o $@
+../device/%.o: ../device/%.cc
+	$(CXX) $(CXXFLAGS) $(CXXFLAGSDEVICE) -I../common -I./inc -I../src -c $< -o $@
 ../device/$(OPENOLTDEVICE)/%.o: ../device/$(OPENOLTDEVICE)/%.cc
 	$(CXX) $(CXXFLAGS) $(CXXFLAGSDEVICE) -I../common -I./inc -I../src -c $< -o $@
 
@@ -141,4 +143,4 @@
 	./test_openolt --gtest_output="xml:./test_openolt_report_xunit.xml"
 
 clean:
-	rm -f src/*.o lib/*.a ../src/*.o ../common/*.o ./test_openolt  ./test_openolt_report_xunit.xml
+	rm -f src/*.o lib/*.a ../src/*.o ../common/*.o ../device/device.o ./test_openolt  ./test_openolt_report_xunit.xml
diff --git a/agent/test/inc/bal_mocker.h b/agent/test/inc/bal_mocker.h
index 8a8a9fa..81d6c19 100644
--- a/agent/test/inc/bal_mocker.h
+++ b/agent/test/inc/bal_mocker.h
@@ -27,13 +27,7 @@
 #include "bcmos_errno.h"
 #include "bcmolt_system_types_typedefs.h"
 #include "bcmolt_msg.h"
-
-/** Host subsystem initialization parameters */
-typedef struct bcmolt_host_init_parms
-{
-    int dummy;
-} bcmolt_host_init_parms;
-
+#include "bcmolt_host_api.h"
 }
 
 class BalMocker : public CMockMocker<BalMocker>
diff --git a/agent/test/src/bal_mocker.cc b/agent/test/src/bal_mocker.cc
index 72ef1d2..5a2d7f1 100644
--- a/agent/test/src/bal_mocker.cc
+++ b/agent/test/src/bal_mocker.cc
@@ -16,7 +16,7 @@
 
 #include "bal_mocker.h"
 extern "C" {
-CMOCK_MOCK_FUNCTION1(BalMocker, bcmolt_host_init, bcmos_errno(bcmolt_host_init_parms*));
+CMOCK_MOCK_FUNCTION1(BalMocker, bcmolt_host_init, bcmos_errno(const bcmolt_host_init_parms*));
 CMOCK_MOCK_FUNCTION2(BalMocker, bcmolt_cfg_get, bcmos_errno(bcmolt_oltid, bcmolt_cfg*));
 CMOCK_MOCK_FUNCTION2(BalMocker, bcmolt_oper_submit, bcmos_errno(bcmolt_oltid, bcmolt_oper*));
 CMOCK_MOCK_FUNCTION2(BalMocker, bcmolt_cfg_set, bcmos_errno(bcmolt_oltid, bcmolt_cfg*));
diff --git a/agent/test/src/test_core.cc b/agent/test/src/test_core.cc
index 75a26ad..dd8265e 100644
--- a/agent/test/src/test_core.cc
+++ b/agent/test/src/test_core.cc
@@ -18,6 +18,7 @@
 #include "bal_mocker.h"
 #include "core.h"
 #include "core_data.h"
+#include "core_utils.h"
 #include "server.h"
 #include <future>
 #include <fstream>
@@ -3956,3 +3957,73 @@
     ASSERT_EQ(rxtx_power_raw.first.second, 0x5C82);  // 23682
     ASSERT_STREQ(trx_eeprom_reader2.get_node_path(), "/sys/bus/i2c/devices/41-0050/eeprom");
 }
+
+////////////////////////////////////////////////////////////////////////////
+// For testing SFP eeprom read and decode capabilities
+////////////////////////////////////////////////////////////////////////////
+
+class TestEEPROMReadDecode : public Test {
+    protected:
+        virtual void SetUp() {
+        }
+        virtual void TearDown() {
+        }
+
+};
+
+// This test reads and decodes EEPROM data confirming to SFP8436 specification.
+// The XFP used on ASFVOLT16 OLTs confirm to this specification.
+TEST_F(TestEEPROMReadDecode, TestSFF8436Decode) {
+    bool res;
+    res = ponTrx.read_eeprom_data_for_sfp(0);
+    ASSERT_TRUE(res);
+    res = ponTrx.decode_eeprom_data(0);
+    ASSERT_TRUE(res);
+    trx_data* t = ponTrx.get_trx_data(0);
+    ASSERT_NE(t, NULL);
+    ASSERT_EQ(t->sfp_index, 0);
+    ASSERT_STREQ(t->vendor_name.c_str(), "Hisense         ");
+    ASSERT_STREQ(t->vendor_part_no.c_str(), "LTH7226-PC+     ");
+    ASSERT_STREQ(t->vendor_rev.c_str(), "01");
+    ASSERT_EQ(t->p_data[0].wavelength, 1577);
+
+}
+
+TEST_F(TestEEPROMReadDecode, TestHexToAsciiSuccess) {
+    std::string vn_ascii("SUPERXON LTD.   ");
+    std::string oui_ascii("");
+    std::string pn_ascii("SOGP4321-PSGB   ");
+    std::string rev_ascii("10");
+    pair<string, bool> res;
+    unsigned char vn_hex[EEPROM_VENDOR_NAME_LENGTH] = {0x53, 0x55, 0x50, 0x45, 0x52, 0x58, 0x4F, 0x4E,
+                                                       0x20, 0x4C, 0x54, 0x44, 0x2E, 0x20, 0x20, 0x20};
+    unsigned char oui_hex[EEPROM_VENDOR_OUI_LENGTH] = {0x00, 0x00, 0x00};
+    unsigned char pn_hex[EEPROM_VENDOR_PART_NUMBER_LENGTH] = {0x53, 0x4F, 0x47, 0x50, 0x34, 0x33, 0x32, 0x31,
+                                                              0x2D, 0x50, 0x53, 0x47, 0x42, 0x20, 0x20, 0x20};
+    unsigned char rev_hex[EEPROM_VENDOR_REVISION_LENGTH] = {0x31, 0x30};
+    res = hex_to_ascii_string(vn_hex, EEPROM_VENDOR_NAME_LENGTH);
+    ASSERT_TRUE(res.second);
+    ASSERT_STREQ(res.first.c_str(), vn_ascii.c_str());
+
+    res = hex_to_ascii_string(oui_hex, EEPROM_VENDOR_OUI_LENGTH);
+    ASSERT_TRUE(res.second);
+    ASSERT_STREQ(res.first.c_str(), oui_ascii.c_str());
+
+    res = hex_to_ascii_string(pn_hex, EEPROM_VENDOR_PART_NUMBER_LENGTH);
+    ASSERT_TRUE(res.second);
+    ASSERT_STREQ(res.first.c_str(), pn_ascii.c_str());
+
+    res = hex_to_ascii_string(rev_hex, EEPROM_VENDOR_REVISION_LENGTH);
+    ASSERT_TRUE(res.second);
+    ASSERT_STREQ(res.first.c_str(), rev_ascii.c_str());
+
+}
+
+TEST_F(TestEEPROMReadDecode, TestHexToUintSuccess) {
+    uint32_t wl_uint = 1490;
+    pair<uint32_t, bool> res;
+    unsigned char vn_hex[EEPROM_DOWNSTREAM_WAVELENGTH_LENGTH] = {0x05, 0xD2};
+    res = hex_to_uinteger(vn_hex, EEPROM_DOWNSTREAM_WAVELENGTH_LENGTH);
+    ASSERT_TRUE(res.second);
+    ASSERT_EQ(res.first, wl_uint);
+}
\ No newline at end of file