[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/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;
+}