Girish Gowdra | b0337eb | 2022-03-25 16:44:21 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2018-present Open Networking Foundation |
| 3 | |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include "device.h" |
| 18 | #include "vendor.h" |
| 19 | #include "core_utils.h" |
| 20 | |
| 21 | using namespace std; |
| 22 | |
| 23 | // PonTrxBase class member function definitions |
| 24 | // Note: 'cout' logger has been used in this file instead of OPENOLT_LOG |
| 25 | // because the logger task may not be initialized in the openolt core module |
| 26 | // before PonTrx module is initialized in the main handler. In that case, |
| 27 | // any OPENOLT_LOG logger (which internally uses BCM_LOG) will not be logged. |
| 28 | |
| 29 | PonTrxBase::PonTrxBase(string eeprom_file_name, |
| 30 | int port_addr, |
| 31 | string eeprom_file_path_name, |
| 32 | string sfp_presence_command) : |
| 33 | _eeprom_file_name{eeprom_file_name}, |
| 34 | _port_addr{port_addr}, |
| 35 | _eeprom_file_path_format{eeprom_file_path_name}, |
| 36 | _sfp_presence_command{sfp_presence_command} |
| 37 | { |
| 38 | } |
| 39 | |
| 40 | PonTrxBase::~PonTrxBase() { |
| 41 | for (auto it : _t_data) { |
| 42 | delete it; |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | // returns the _sfp_presence_data |
| 47 | const set<int> PonTrxBase::get_sfp_presence_data() { |
| 48 | return _sfp_presence_data; |
| 49 | } |
| 50 | |
| 51 | // reads, updates and returns the _sfp_presence_data |
| 52 | const set<int> PonTrxBase::read_sfp_presence_data() { |
| 53 | set<int> sp; |
| 54 | // command to read the sfp presence array |
| 55 | // The sample output of the command looks like below |
| 56 | // root@localhost:~# onlpdump -p |
| 57 | // Presence: 0 1 16 |
| 58 | const string& command = _sfp_presence_command; |
| 59 | int status_code = system((command + " > temp.txt").c_str()); |
| 60 | if (status_code != 0) { |
| 61 | perror("error getting sfp presence array from ONL\n"); |
| 62 | // return the current _sfp_presence_data |
| 63 | return _sfp_presence_data; |
| 64 | } |
| 65 | ifstream ifs("temp.txt"); |
| 66 | string res = {istreambuf_iterator<char>(ifs), istreambuf_iterator<char>()}; |
| 67 | ifs.close(); // must close the inout stream so the file can be cleaned up |
| 68 | // If there is issue reading the sfp presence array, print error and return existing data |
| 69 | if (remove("temp.txt") != 0) { |
| 70 | perror("Error deleting temporary file"); |
| 71 | // return the current sfp presence array |
| 72 | return _sfp_presence_data; |
| 73 | } |
| 74 | |
| 75 | // parse the output of sfp presence command and read which PON Trx are connected/disconnected now |
| 76 | stringstream ss(res); |
| 77 | string s; |
| 78 | while (getline(ss, s, ' ')) { |
| 79 | try { |
| 80 | int i = stoi(s); |
| 81 | // Set the Trx port index corresponding the PON port to true if it appears on the sfp presence command output |
| 82 | if (i < TOTAL_PON_TRX_PORTS) { |
| 83 | sp.insert(i); |
| 84 | } |
| 85 | |
| 86 | } catch(invalid_argument) { |
| 87 | // do nothing |
| 88 | } catch(out_of_range) { |
| 89 | // do nothing |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | // If the current _sfp_presence_data is not same the newly read data, |
| 94 | // update local copy and also raise an event. |
| 95 | if (sp != _sfp_presence_data) { |
| 96 | cout << "sfp presence data has been updated\n"; |
| 97 | |
| 98 | set<int> sfp_ports_up, sfp_ports_down; |
| 99 | |
| 100 | set_difference(sp.begin(), sp.end(), _sfp_presence_data.begin(), _sfp_presence_data.end(), inserter(sfp_ports_up, sfp_ports_up.begin())); |
| 101 | // New SFPs are detected |
| 102 | if (sfp_ports_up.size() > 0) { |
| 103 | cout << "following pon sfp ports are up: "; |
| 104 | for (auto up_it : sfp_ports_up) { |
| 105 | cout << up_it << " "; |
| 106 | } |
| 107 | cout << "\n"; |
| 108 | } |
| 109 | set_difference(_sfp_presence_data.begin(), _sfp_presence_data.end(), sp.begin(), sp.end(), inserter(sfp_ports_down, sfp_ports_down.begin())); |
| 110 | // Some SFPs are down |
| 111 | if (sfp_ports_down.size() > 0) { |
| 112 | cout << "following pon sfp ports are down: \n"; |
| 113 | for (auto down_it : sfp_ports_down) { |
| 114 | cout << down_it << " "; |
| 115 | } |
| 116 | cout << "\n"; |
| 117 | } |
| 118 | |
| 119 | // FIXME: raise an event for all the PON Trx ports that have been updated |
| 120 | _sfp_presence_data = sp; |
| 121 | } |
| 122 | |
| 123 | return _sfp_presence_data; |
| 124 | } |
| 125 | |
| 126 | // reads the EEPROM data. The sfp_index is the bus index corresponding to the PON Trx |
| 127 | // returns true if success |
| 128 | bool PonTrxBase::read_eeprom_data_for_sfp(int sfp_index) { |
| 129 | ifstream is; |
| 130 | int len; |
| 131 | array<char, EEPROM_READ_PATH_SIZE> path{}; |
| 132 | array<unsigned char, EEPROM_DATA_READ_SIZE> data{}; |
| 133 | |
| 134 | if (sfp_index >= TOTAL_PON_TRX_PORTS) { |
| 135 | perror("invalid pon trx index\n"); |
| 136 | return false; |
| 137 | } |
| 138 | |
| 139 | if (_eeprom_read_path.count(sfp_index) == 0) { |
| 140 | _eeprom_read_path[sfp_index] = path; |
| 141 | char file_name[PON_TRX_BUS_FORMAT_SIZE]; |
| 142 | char *tmp_place_holder = new char[PON_TRX_BUS_FORMAT_SIZE]; |
| 143 | // 4 is max length of _port_addr |
| 144 | if ((_eeprom_file_path_format.length() + _eeprom_file_name.length() + 4) > PON_TRX_BUS_FORMAT_SIZE) { |
| 145 | cerr << "resultant eeprom file length too long\n"; |
| 146 | delete []tmp_place_holder; |
| 147 | return false; |
| 148 | } |
| 149 | strcpy(tmp_place_holder, _eeprom_file_path_format.c_str()); |
| 150 | sprintf(file_name, tmp_place_holder, bus_index[sfp_index], _port_addr, _eeprom_file_name.c_str()); |
| 151 | memcpy(&_eeprom_read_path[sfp_index][0], file_name, PON_TRX_BUS_FORMAT_SIZE); |
| 152 | delete []tmp_place_holder; |
| 153 | } |
| 154 | cout << "eeprom file to read is: " << _eeprom_read_path[sfp_index].data() << "for sfp " << sfp_index << endl; |
| 155 | FILE* fp = fopen(_eeprom_read_path[sfp_index].data(), "rb"); |
| 156 | if (!fp) { |
| 157 | perror("failed to open file"); |
| 158 | _eeprom_read_path.erase(sfp_index); |
| 159 | return false; |
| 160 | } |
| 161 | _eeprom_data[sfp_index] = data; |
| 162 | int val, cnt = 0; |
| 163 | |
| 164 | // Populate EEPROM data |
| 165 | while ((val = fgetc(fp)) != EOF && cnt < EEPROM_DATA_READ_SIZE) { |
| 166 | _eeprom_data[sfp_index][cnt] = (unsigned char)val; |
| 167 | cnt++; |
| 168 | } |
| 169 | fclose(fp); |
| 170 | if (cnt != EEPROM_DATA_READ_SIZE) { |
| 171 | cerr << "invalid length of data read:" << cnt << endl; |
| 172 | _eeprom_read_path.erase(sfp_index); |
| 173 | _eeprom_data.erase(sfp_index); |
| 174 | return false; |
| 175 | } |
| 176 | |
| 177 | return true; |
| 178 | } |
| 179 | |
| 180 | // decodes the EEPROM data. The sfp_index is the bus index corresponding to the PON Trx |
| 181 | // returns true if success |
| 182 | bool PonTrxBase::decode_eeprom_data(int sfp_index) { |
| 183 | /* |
| 184 | try { |
| 185 | */ |
| 186 | |
| 187 | trx_data tmp, *store=NULL; |
| 188 | tmp.sfp_index = sfp_index; |
| 189 | |
| 190 | // decode vendor name |
| 191 | array<unsigned char, EEPROM_VENDOR_NAME_LENGTH> vn{}; |
| 192 | copy(&_eeprom_data[sfp_index][EEPROM_VENDOR_NAME_START_IDX], |
| 193 | &_eeprom_data[sfp_index][EEPROM_VENDOR_NAME_START_IDX] + EEPROM_VENDOR_NAME_LENGTH, |
| 194 | &vn[0]); |
| 195 | pair<string, bool> res_str = hex_to_ascii_string(vn.data(), vn.max_size()); |
| 196 | if (!res_str.second) { |
| 197 | perror("error decoding vendor name\n"); |
| 198 | return false; |
| 199 | } |
| 200 | tmp.vendor_name = res_str.first; |
| 201 | cout << "[" << sfp_index << "]" << " vendor name: " << tmp.vendor_name << endl; |
| 202 | |
| 203 | |
| 204 | // decode vendor oui |
| 205 | array<unsigned char, EEPROM_VENDOR_OUI_LENGTH> oui{}; |
| 206 | copy(&_eeprom_data[sfp_index][EEPROM_VENDOR_OUI_START_IDX], |
| 207 | &_eeprom_data[sfp_index][EEPROM_VENDOR_OUI_START_IDX] + EEPROM_VENDOR_OUI_LENGTH, |
| 208 | &oui[0]); |
| 209 | res_str = hex_to_ascii_string(oui.data(), oui.max_size()); |
| 210 | if (!res_str.second) { |
| 211 | perror("error decoding vendor oui\n"); |
| 212 | return false; |
| 213 | } |
| 214 | if (res_str.first.length() == 3) { // vendor_oui length is 3 |
| 215 | memcpy(tmp.vendor_oui, res_str.first.c_str(), 3); |
| 216 | } |
| 217 | cout << "[" << sfp_index << "]" << " vendor oui: " << res_str.first << endl; |
| 218 | |
| 219 | // decode vendor part number |
| 220 | array<unsigned char, EEPROM_VENDOR_PART_NUMBER_LENGTH> pn{}; |
| 221 | copy(&_eeprom_data[sfp_index][EEPROM_VENDOR_PART_NUMBER_START_IDX], |
| 222 | &_eeprom_data[sfp_index][EEPROM_VENDOR_PART_NUMBER_START_IDX] + EEPROM_VENDOR_PART_NUMBER_LENGTH, |
| 223 | &pn[0]); |
| 224 | res_str = hex_to_ascii_string(pn.data(), pn.max_size()); |
| 225 | if (!res_str.second) { |
| 226 | perror("error decoding vendor part number\n"); |
| 227 | return false; |
| 228 | } |
| 229 | tmp.vendor_part_no = res_str.first; |
| 230 | cout << "[" << sfp_index << "]" << " vendor pn: " << tmp.vendor_part_no << endl; |
| 231 | |
| 232 | // decode vendor revision |
| 233 | array<unsigned char, EEPROM_VENDOR_REVISION_LENGTH> rev{}; |
| 234 | copy(&_eeprom_data[sfp_index][EEPROM_VENDOR_REVISION_START_IDX], |
| 235 | &_eeprom_data[sfp_index][EEPROM_VENDOR_REVISION_START_IDX] + EEPROM_VENDOR_REVISION_LENGTH, |
| 236 | &rev[0]); |
| 237 | res_str = hex_to_ascii_string(rev.data(), rev.max_size()); |
| 238 | if (!res_str.second) { |
| 239 | perror("error decoding vendor revision\n"); |
| 240 | return false; |
| 241 | } |
| 242 | tmp.vendor_rev = res_str.first; |
| 243 | cout << "[" << sfp_index << "]" << " vendor rev: " << tmp.vendor_rev << endl; |
| 244 | |
| 245 | // decode pon wavelength(s) |
| 246 | array<unsigned char, EEPROM_DOWNSTREAM_WAVELENGTH_LENGTH> wv{}; |
| 247 | copy(&_eeprom_data[sfp_index][EEPROM_DOWNSTREAM_WAVELENGTH_START_IDX], |
| 248 | &_eeprom_data[sfp_index][EEPROM_DOWNSTREAM_WAVELENGTH_START_IDX] + EEPROM_DOWNSTREAM_WAVELENGTH_LENGTH, |
| 249 | &wv[0]); |
| 250 | pair<uint32_t, bool> res_uint = hex_to_uinteger(wv.data(), wv.max_size()); |
| 251 | if (!res_uint.second) { |
| 252 | perror("error decoding primary downstream wavelength\n"); |
| 253 | return false; |
| 254 | } |
| 255 | tmp.p_data[0].valid = true; |
| 256 | tmp.p_data[0].wavelength = res_uint.first * EEPROM_WAVELENGTH_RESOLUTION; |
| 257 | tmp.p_data[0].pon_type = get_pon_type_from_wavelength(tmp.p_data[0].wavelength); |
| 258 | cout << "[" << sfp_index << "]" << " pon wavelength: " << tmp.p_data[0].wavelength << endl; |
| 259 | // TODO: also fill the field pon_type |
| 260 | |
| 261 | // TODO: Let's ignore secondary wavelength decode for now as we do not know how the secondary wavelength is represent on the Combo PON |
| 262 | tmp.p_data[1].valid = false; |
| 263 | |
| 264 | store = new(nothrow) trx_data; |
| 265 | if (store == NULL) { |
| 266 | perror("could not allocate memory for trx_data"); |
| 267 | return false; |
| 268 | } |
| 269 | |
| 270 | // Copy data |
| 271 | store->sfp_index = sfp_index; |
| 272 | store->vendor_name = tmp.vendor_name; |
| 273 | memcpy(store->vendor_oui, tmp.vendor_oui, 3); |
| 274 | store->vendor_part_no = tmp.vendor_part_no; |
| 275 | store->vendor_rev = tmp.vendor_rev; |
| 276 | store->trx_type = tmp.trx_type; |
| 277 | for (int i = 0; i < MAX_PONS_PER_TRX; i++) { |
| 278 | store->p_data[i].valid = tmp.p_data[i].valid; |
| 279 | store->p_data[i].wavelength = tmp.p_data[i].wavelength; |
| 280 | store->p_data[i].pon_type = tmp.p_data[i].pon_type; |
| 281 | } |
| 282 | |
| 283 | _t_data.insert(store); |
| 284 | /* |
| 285 | } catch(...) { // FIXME: Put specific exceptions here |
| 286 | perror("caught exception\n"); |
| 287 | return false; |
| 288 | } |
| 289 | */ |
| 290 | return true; |
| 291 | } |
| 292 | |
| 293 | bcmolt_pon_type PonTrxBase::get_pon_type_from_wavelength(int wavelength) { |
| 294 | bcmolt_pon_type pon_type = BCMOLT_PON_TYPE_UNKNOWN; |
| 295 | switch (wavelength) { |
| 296 | case GPON_DOWNSTREAM_WAVELENGTH_NM: |
| 297 | pon_type = BCMOLT_PON_TYPE_GPON; |
| 298 | case XGSPON_DOWNSTREAM_WAVELENGTH_NM: |
| 299 | pon_type = BCMOLT_PON_TYPE_XGPON; |
| 300 | // Add more in the future as needed. |
| 301 | } |
| 302 | return pon_type; |
| 303 | } |
| 304 | |
| 305 | bcmolt_pon_type PonTrxBase::get_sfp_mode(int sfp_index) { |
| 306 | bcmolt_pon_type pon0_type = BCMOLT_PON_TYPE_UNKNOWN; |
| 307 | bcmolt_pon_type pon1_type = BCMOLT_PON_TYPE_UNKNOWN; |
| 308 | trx_data *td = NULL; |
| 309 | set<trx_data*>::iterator it; |
| 310 | // Iterate the trx data set and break if we find a trx data with mathcing sfp index |
| 311 | for (it = _t_data.begin(); it != _t_data.end(); it++) { |
| 312 | if ((*it)->sfp_index == sfp_index) { |
| 313 | td = *it; |
| 314 | break; |
| 315 | } |
| 316 | } |
| 317 | if (it == _t_data.end()) { |
| 318 | perror("end of iterator, pon type not detected"); |
| 319 | return pon0_type; |
| 320 | } |
| 321 | if (td == NULL) { |
| 322 | cerr << "trx data is null\n"; |
| 323 | return pon0_type; |
| 324 | } |
| 325 | |
| 326 | // Find the PON type/mode |
| 327 | if (td->p_data[0].valid) { |
| 328 | pon0_type = td->p_data[0].pon_type; |
| 329 | if (pon0_type == BCMOLT_PON_TYPE_UNKNOWN) { |
| 330 | return pon0_type; |
| 331 | } |
| 332 | if (td->p_data[1].valid) { |
| 333 | pon1_type = td->p_data[1].pon_type; |
| 334 | if (pon1_type == BCMOLT_PON_TYPE_UNKNOWN) { |
| 335 | return pon1_type; |
| 336 | } |
| 337 | |
| 338 | if (pon0_type == pon1_type) { |
| 339 | return pon0_type; |
| 340 | } else { |
| 341 | return BCMOLT_PON_TYPE_XGPON_GPON_WDMA; // Combo SFP! |
| 342 | } |
| 343 | } |
| 344 | return pon0_type; |
| 345 | } |
| 346 | |
| 347 | return pon0_type; |
| 348 | } |
| 349 | |
| 350 | // Returns the MAC System Mode based on the set of SFP IDs provided. |
| 351 | // The derived class will most likely need to override this method to provide a |
| 352 | // different implementation for that particular OLT platform. |
| 353 | pair<bcmolt_system_mode, bool> PonTrxBase::get_mac_system_mode(int olt_mac_id, set<int> sfp_ids) { |
| 354 | bool ret = true; |
| 355 | bcmolt_system_mode sm = BCMOLT_SYSTEM_MODE_GPON__16_X; |
| 356 | |
| 357 | return {sm, ret}; |
| 358 | } |
| 359 | |
| 360 | // Get trx data for sfp |
| 361 | trx_data* PonTrxBase::get_trx_data(int sfp_index) { |
| 362 | for (auto it : _t_data) { |
| 363 | if (it->sfp_index == sfp_index) { |
| 364 | cout << "[" << sfp_index << "]" << " found trx data\n"; |
| 365 | return (it); |
| 366 | } |
| 367 | } |
| 368 | return NULL; |
| 369 | } |