| /* |
| * 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; |
| } |