[VOL-4069] Measure and read Rx optical power for an ONU
Reading Rx optical power requires triggering an RSSI measurement and tracking the RSSI Measurement Completed indication.
The raw value is converted to mW, and a dBm value is returned back.
A small test application (~40K) is bundled within the Debian package to help with debugging the optical power levels.
Change-Id: I20f304a9de0c47b94dfd7b1d8fdd52c56d6a2983
diff --git a/agent/src/core_api_handler.cc b/agent/src/core_api_handler.cc
index 6252355..babd9ce 100644
--- a/agent/src/core_api_handler.cc
+++ b/agent/src/core_api_handler.cc
@@ -75,6 +75,7 @@
bcmolt_egress_qos_type qos_type, uint32_t priority, uint32_t gemport_id, uint32_t tech_profile_id);
static bcmos_errno CreateDefaultSched(uint32_t intf_id, const std::string direction);
static bcmos_errno CreateDefaultQueue(uint32_t intf_id, const std::string direction);
+static const std::chrono::milliseconds ONU_RSSI_COMPLETE_WAIT_TIMEOUT = std::chrono::seconds(10);
inline const char *get_flow_acton_command(uint32_t command) {
char actions[200] = { };
@@ -3298,3 +3299,69 @@
OPENOLT_LOG(INFO, openolt_log_id, "retrieved GEMPORT statistics for PON ID = %d, GEMPORT ID = %d\n", (int)intf_id, (int)gemport_id);
return Status::OK;
}
+
+Status GetPonRxPower_(uint32_t intf_id, uint32_t onu_id, openolt::PonRxPowerData* response) {
+ bcmos_errno err = BCM_ERR_OK;
+
+ // check the PON intf id
+ if (intf_id >= MAX_SUPPORTED_PON) {
+ err = BCM_ERR_PARM;
+ OPENOLT_LOG(ERROR, openolt_log_id, "invalid pon intf_id - intf_id: %d, onu_id: %d\n",
+ intf_id, onu_id);
+ return bcm_to_grpc_err(err, "invalid pon intf_id");
+ }
+
+ bcmolt_onu_rssi_measurement onu_oper; /* declare main API struct */
+ bcmolt_onu_key onu_key; /**< Object key. */
+ onu_rssi_compltd_key key(intf_id, onu_id);
+ Queue<onu_rssi_complete_result> queue;
+
+ OPENOLT_LOG(INFO, openolt_log_id, "GetPonRxPower - intf_id %d, onu_id %d\n", intf_id, onu_id);
+
+ onu_key.onu_id = onu_id;
+ onu_key.pon_ni = intf_id;
+ /* Initialize the API struct. */
+ BCMOLT_OPER_INIT(&onu_oper, onu, rssi_measurement, onu_key);
+ err = bcmolt_oper_submit(dev_id, &onu_oper.hdr);
+ if (err == BCM_ERR_OK) {
+ // initialize map
+ bcmos_fastlock_lock(&onu_rssi_wait_lock);
+ onu_rssi_compltd_map.insert({key, &queue});
+ bcmos_fastlock_unlock(&onu_rssi_wait_lock, 0);
+ } else {
+ OPENOLT_LOG(ERROR, openolt_log_id, "failed to measure rssi rx power - intf_id: %d, onu_id: %d, err = %s (%d): %s\n",
+ intf_id, onu_id, bcmos_strerror(err), err, onu_oper.hdr.hdr.err_text);
+ return bcm_to_grpc_err(err, "failed to measure rssi rx power");
+ }
+
+ onu_rssi_complete_result completed{};
+ if (!queue.pop(completed, ONU_RSSI_COMPLETE_WAIT_TIMEOUT)) {
+ // invalidate the queue pointer
+ bcmos_fastlock_lock(&onu_rssi_wait_lock);
+ onu_rssi_compltd_map[key] = NULL;
+ bcmos_fastlock_unlock(&onu_rssi_wait_lock, 0);
+ err = BCM_ERR_TIMEOUT;
+ OPENOLT_LOG(ERROR, openolt_log_id, "timeout waiting for RSSI Measurement Completed indication intf_id %d, onu_id %d\n",
+ intf_id, onu_id);
+ } else {
+ OPENOLT_LOG(INFO, openolt_log_id, "RSSI Rx power - intf_id: %d, onu_id: %d, status: %s, fail_reason: %d, rx_power_mean_dbm: %f\n",
+ completed.pon_intf_id, completed.onu_id, completed.status.c_str(), completed.reason, completed.rx_power_mean_dbm);
+
+ response->set_intf_id(completed.pon_intf_id);
+ response->set_onu_id(completed.onu_id);
+ response->set_status(completed.status);
+ response->set_fail_reason(static_cast<::openolt::PonRxPowerData_RssiMeasurementFailReason>(completed.reason));
+ response->set_rx_power_mean_dbm(completed.rx_power_mean_dbm);
+ }
+
+ // Remove entry from map
+ bcmos_fastlock_lock(&onu_rssi_wait_lock);
+ onu_rssi_compltd_map.erase(key);
+ bcmos_fastlock_unlock(&onu_rssi_wait_lock, 0);
+
+ if (err == BCM_ERR_OK) {
+ return Status::OK;
+ } else {
+ return bcm_to_grpc_err(err, "timeout waiting for pon rssi measurement complete indication");
+ }
+}
diff --git a/agent/src/core_data.cc b/agent/src/core_data.cc
index ad66ae3..a53ca0e 100644
--- a/agent/src/core_data.cc
+++ b/agent/src/core_data.cc
@@ -206,3 +206,7 @@
char* grpc_server_interface_name = NULL;
+
+// 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;
diff --git a/agent/src/core_data.h b/agent/src/core_data.h
index 214b8fd..12edb9e 100644
--- a/agent/src/core_data.h
+++ b/agent/src/core_data.h
@@ -162,6 +162,17 @@
} device_flow;
+// key for map used for tracking Onu RSSI Measurement Completed Indication
+typedef std::tuple<uint32_t, uint32_t> onu_rssi_compltd_key;
+
+typedef struct {
+ uint32_t pon_intf_id;
+ uint32_t onu_id;
+ std::string status;
+ bcmolt_rssi_measurement_fail_reason reason;
+ double rx_power_mean_dbm;
+} onu_rssi_complete_result;
+
// *******************************************************//
// Extern Variable/Constant declarations used by the core //
// *******************************************************//
@@ -333,4 +344,7 @@
// Interface name on which grpc server is running on
// and this can be used to get the mac adress based on interface name.
extern char* grpc_server_interface_name;
+
+extern std::map<onu_rssi_compltd_key, Queue<onu_rssi_complete_result>*> onu_rssi_compltd_map;
+extern bcmos_fastlock onu_rssi_wait_lock;
#endif // OPENOLT_CORE_DATA_H_
diff --git a/agent/src/core_utils.cc b/agent/src/core_utils.cc
index 0153078..e8060aa 100644
--- a/agent/src/core_utils.cc
+++ b/agent/src/core_utils.cc
@@ -1723,3 +1723,25 @@
return {buffer.str(), in_file.good()};
}
+
+bool save_to_txt_file(const std::string& file_name, const std::string& content) {
+ std::ofstream out_file;
+ out_file.exceptions(std::ofstream::failbit | std::ofstream::badbit);
+
+ try {
+ out_file.open(file_name, std::ios::out | std::ios::trunc);
+
+ if (!out_file.is_open()) {
+ std::cerr << "error while opening file '" << file_name << "'\n";
+ return false;
+ }
+
+ out_file << content;
+ out_file.close();
+
+ return true;
+ } catch (const std::ofstream::failure& e) {
+ std::cerr << "exception while writing to file '" << file_name << "' | err: " << e.what() << '\n';
+ return false;
+ }
+}
diff --git a/agent/src/core_utils.h b/agent/src/core_utils.h
index 3d4668c..c860644 100644
--- a/agent/src/core_utils.h
+++ b/agent/src/core_utils.h
@@ -122,4 +122,5 @@
const std::string &get_grpc_tls_option();
bool is_grpc_secure();
std::pair<std::string, bool> read_from_txt_file(const std::string& file_name);
+bool save_to_txt_file(const std::string& file_name, const std::string& content);
#endif // OPENOLT_CORE_UTILS_H_
diff --git a/agent/src/indications.cc b/agent/src/indications.cc
index 0583ddb..304cb45 100644
--- a/agent/src/indications.cc
+++ b/agent/src/indications.cc
@@ -21,6 +21,7 @@
#include "stats_collection.h"
#include "translation.h"
#include "state.h"
+#include "trx_eeprom_reader.h"
#include <string>
@@ -1055,6 +1056,66 @@
bcmolt_msg_free(msg);
}
+static void OnuRssiMeasurementCompletedIndication(bcmolt_devid olt, bcmolt_msg *msg) {
+ switch (msg->obj_type) {
+ case BCMOLT_OBJ_ID_ONU:
+ switch (msg->subgroup) {
+ case BCMOLT_ONU_AUTO_SUBGROUP_RSSI_MEASUREMENT_COMPLETED:
+ {
+ bcmolt_onu_key *key = &((bcmolt_onu_rssi_measurement_completed*)msg)->key;
+ bcmolt_onu_rssi_measurement_completed_data *data = &((bcmolt_onu_rssi_measurement_completed*)msg)->data;
+ double rx_power_mean_dbm = 0.0;
+
+ OPENOLT_LOG(INFO, openolt_log_id, "ONU RSSI Measurement Completed indication - pon_id: %d, onu_id: %d, status: %d, fail_reason: %d\n",
+ key->pon_ni, key->onu_id, data->status, data->fail_reason);
+
+ if (data->status == BCMOLT_RESULT_SUCCESS) {
+ auto trx_eeprom_reader =
+#ifdef ASGVOLT64
+ TrxEepromReader{TrxEepromReader::DEVICE_GPON, TrxEepromReader::RX_POWER, key->pon_ni};
+#else
+ TrxEepromReader{TrxEepromReader::DEVICE_XGSPON, TrxEepromReader::RX_POWER, key->pon_ni};
+#endif
+ auto power = trx_eeprom_reader.read_power_mean_dbm();
+
+ if (power.second) {
+ rx_power_mean_dbm = power.first.first;
+ OPENOLT_LOG(INFO, openolt_log_id, "ONU RSSI Measurement Completed indication - rx_power_mean_dbm: %f\n", rx_power_mean_dbm);
+ } else {
+ OPENOLT_LOG(ERROR, openolt_log_id, "ONU RSSI Measurement Completed indication - Rx power read failure\n");
+ }
+ } else {
+ OPENOLT_LOG(ERROR, openolt_log_id, "ONU RSSI Measurement Completed indication - failure");
+ }
+
+ // for internal use insert into map
+ onu_rssi_compltd_key onu_key((uint32_t)key->pon_ni, (uint32_t)key->onu_id);
+ onu_rssi_complete_result res;
+ res.pon_intf_id = (uint32_t)key->pon_ni;
+ res.onu_id = (uint32_t)key->onu_id;
+ res.status = bcmolt_result_to_string(data->status);
+ res.reason = data->fail_reason;
+ res.rx_power_mean_dbm = rx_power_mean_dbm;
+
+ bcmos_fastlock_lock(&onu_rssi_wait_lock);
+ auto it = onu_rssi_compltd_map.find(onu_key);
+ if (it == onu_rssi_compltd_map.end()) {
+ OPENOLT_LOG(ERROR, openolt_log_id, "ONU RSSI Measurement Completed key not found for pon intf %u, onu_id %u\n",
+ key->pon_ni, key->onu_id);
+ } else if (it->second) {
+ it->second->push(res);
+ } else {
+ OPENOLT_LOG(WARNING, openolt_log_id, "ONU RSSI Measurement Completed queue not found for pon intf %u, onu_id %u\n",
+ key->pon_ni, key->onu_id);
+ }
+ bcmos_fastlock_unlock(&onu_rssi_wait_lock, 0);
+ }
+ }
+ }
+
+ bcmolt_msg_free(msg);
+}
+
/* removed by BAL v3.0
bcmos_errno OnuProcessingErrorIndication(bcmbal_obj *obj) {
openolt::Indication ind;
@@ -1320,6 +1381,14 @@
if(rc != BCM_ERR_OK)
return Status(grpc::StatusCode::INTERNAL, "Complete members update indication subscribe failed");
+ rx_cfg.obj_type = BCMOLT_OBJ_ID_ONU;
+ rx_cfg.rx_cb = OnuRssiMeasurementCompletedIndication;
+ rx_cfg.flags = BCMOLT_AUTO_FLAGS_NONE;
+ rx_cfg.subgroup = bcmolt_onu_auto_subgroup_rssi_measurement_completed;
+ rc = bcmolt_ind_subscribe(current_device, &rx_cfg);
+ if(rc != BCM_ERR_OK)
+ return Status(grpc::StatusCode::INTERNAL, "ONU RSSI Measurement indication subscription failed");
+
subscribed = true;
return Status::OK;
diff --git a/agent/src/trx_eeprom_reader.cc b/agent/src/trx_eeprom_reader.cc
new file mode 100644
index 0000000..0c17a3d
--- /dev/null
+++ b/agent/src/trx_eeprom_reader.cc
@@ -0,0 +1,325 @@
+/*
+ * 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 <string>
+#include <cstring>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <cstdio>
+#include <cstdlib>
+#include <cmath>
+#include <iomanip>
+#include <stdexcept>
+
+#include "trx_eeprom_reader.h"
+
+// g++ -std=c++11 -DRXTX_POWER_EXE_MODE trx_eeprom_reader.cc -o rssi
+
+TrxEepromReader::TrxEepromReader(device_type dev_type, const power_type read_type, const int port)
+ : _read_type{read_type},
+ _port{port} {
+
+ if (dev_type == DEVICE_EITHER) {
+ std::string device = TrxEepromReader::get_board_name();
+ if (device == "ASGvOLT64") {
+ dev_type = TrxEepromReader::DEVICE_GPON;
+ } else if (device == "ASXvOLT16") {
+ dev_type = TrxEepromReader::DEVICE_XGSPON;
+ } else { // improbable
+ std::cerr << "ERROR - unknown device: '" << device << "'\n";
+ dev_type = TrxEepromReader::DEVICE_XGSPON;
+ }
+ }
+
+ switch (dev_type) {
+ case DEVICE_GPON:
+ _dev_name = "ASGvOLT64";
+ _buf_size = 600;
+ _read_offset = 360,
+ _read_num_bytes = 2;
+ _port_addr = 50;
+ _name_eeprom = "eeprom";
+ _bus_index = _gpon_bus_index;
+ _max_ports = sizeof(_gpon_bus_index) / sizeof(_gpon_bus_index[0]);
+ break;
+
+ case DEVICE_XGSPON:
+ _dev_name = "ASXvOLT16";
+ _buf_size = 256;
+ _read_offset = 104,
+ _read_num_bytes = 2;
+ _port_addr = 50;
+ _name_eeprom = "sfp_eeprom";
+ _bus_index = _xgspon_bus_index;
+ _max_ports = sizeof(_xgspon_bus_index) / sizeof(_xgspon_bus_index[0]);
+ break;
+ }
+
+ // trick: convert the first string specifier to integer specifier
+ sprintf(_node_format, _master_format, "%d", _port_addr, _name_eeprom.c_str());
+}
+
+std::pair<std::string, bool> TrxEepromReader::read_txt_file(const std::string& file_name) {
+ std::ifstream in_file(file_name);
+
+ if (!in_file.is_open()) {
+ std::cerr << "error while opening file '" << file_name << "'\n";
+ return {"", false};
+ }
+
+ std::stringstream buffer;
+ buffer << in_file.rdbuf();
+
+ return {buffer.str(), in_file.good()};
+}
+
+std::string TrxEepromReader::get_board_name() {
+ const std::string board_path = "/sys/devices/virtual/dmi/id/board_name";
+ auto res = TrxEepromReader::read_txt_file(board_path);
+
+ // read failure is improbable
+ if (!res.second) {
+ std::cerr << "ERROR - file " << board_path << "cannot be opened\n";
+ }
+
+ if (res.first.find_last_of("\n") != std::string::npos) {
+ res.first.pop_back();
+ }
+
+ return res.first;
+}
+
+int TrxEepromReader::read_binary_file(char* buffer) {
+ std::ifstream is;
+ int len;
+
+ if (buffer == NULL || _buf_size < 0) {
+ return -1;
+ }
+
+#ifdef TEST_MODE
+ is.open("./eeprom.bin", std::ios_base::in | std::ios_base::binary);
+#else
+ is.open(_node_path, std::ios_base::in | std::ios_base::binary);
+#endif
+
+ if (!is.is_open()) {
+ return -2;
+ }
+
+ is.read(buffer, _buf_size);
+ len = is.gcount();
+ is.close();
+
+ if (len > _buf_size || len < (_read_offset + _read_num_bytes)) {
+ return -4;
+ }
+
+ return len;
+}
+
+bool TrxEepromReader::is_valid_port() const {
+ return (_port >= 0 && (size_t)_port < _max_ports);
+}
+
+void TrxEepromReader::set_port_path() {
+ sprintf(_node_path, _node_format, _bus_index[_port]);
+}
+
+unsigned long TrxEepromReader::get_value_from_pointer_u(unsigned char *ptr, int size) {
+ unsigned long sum = 0;
+ unsigned char i;
+
+ if (size > 4) {
+ return (sum);
+ }
+
+ for (i = 0; i < size; ++i) {
+ sum = sum * 256 + (*(ptr + i));
+ }
+
+ return (sum);
+}
+
+double TrxEepromReader::raw_rx_to_mw(int raw) {
+ return raw * 0.0001;
+}
+
+double TrxEepromReader::raw_tx_to_mw(int raw) {
+ return raw * 0.0002;
+}
+
+double TrxEepromReader::mw_to_dbm(double mw) {
+ return 10 * log10(mw);
+}
+
+std::pair<std::pair<int, int>, bool> TrxEepromReader::read_power_raw() {
+ if (is_valid_port()) {
+ set_port_path();
+
+ char* eeprom_data = new (std::nothrow) char[_buf_size];
+
+ if (!eeprom_data) {
+ std::cerr << "ERROR - memory allocation for eeprom_data failed\n";
+ return {{0, 0}, false};
+ }
+
+ int ret_file = read_binary_file(eeprom_data);
+
+ if (ret_file < 0) {
+ std::cerr << "ERROR - eeprom_data file cannot be opened\n";
+ return {{0, 0}, false};
+ }
+
+ // le = Little Endian, be = Big Endian
+ unsigned char rx_power_le[_read_num_bytes] = {0};
+ unsigned char tx_power_le[_read_num_bytes] = {0};
+ unsigned long rx_power_be = 0;
+ unsigned long tx_power_be = 0;
+
+ if (_read_type == RX_POWER || _read_type == RX_AND_TX_POWER) {
+ memcpy(&rx_power_le, eeprom_data + _read_offset, _read_num_bytes);
+ rx_power_be = get_value_from_pointer_u(rx_power_le, _read_num_bytes);
+ }
+
+ if (_read_type == TX_POWER || _read_type == RX_AND_TX_POWER) {
+ memcpy(&tx_power_le, eeprom_data + _read_offset - _read_num_bytes, _read_num_bytes);
+ tx_power_be = get_value_from_pointer_u(tx_power_le, _read_num_bytes);
+ }
+
+ if (_read_type == RX_POWER) {
+ tx_power_be = 0;
+ }
+
+ if (_read_type == TX_POWER) {
+ rx_power_be = 0;
+ }
+
+ delete[] eeprom_data;
+
+ return {{(int)rx_power_be, (int)tx_power_be}, true};
+ } else {
+ std::cerr << "ERROR - invalid port: " << _port << '\n';
+ return {{0, 0}, false};
+ }
+}
+
+std::pair<std::pair<double, double>, bool> TrxEepromReader::read_power_mean_dbm() {
+ auto power_raw = read_power_raw();
+ if (power_raw.second) {
+ return {{mw_to_dbm(raw_rx_to_mw(power_raw.first.first)), mw_to_dbm(raw_tx_to_mw(power_raw.first.second))}, true};
+ } else {
+ return {{0.0, 0.0}, false};
+ }
+}
+
+std::string TrxEepromReader::dump_data() {
+ std::ostringstream dump;
+
+ dump << "\tdevice: " << _dev_name << '\n'
+ << "\tbuffer size: " << _buf_size << '\n'
+ << "\tread offset: " << _read_offset << '\n'
+ << "\tnum bytes: " << _read_num_bytes << '\n'
+ << "\tmax ports: " << _max_ports << '\n'
+ << "\tport: " << _port << '\n';
+
+ //dump << std::showpos;
+ dump << std::fixed;
+ dump << std::setprecision(3);
+
+ auto rxtx_power_raw = this->read_power_raw();
+
+ dump << "\tnode path: " << _node_path << '\n';
+
+ if (rxtx_power_raw.second) {
+ if (_read_type == RX_POWER || _read_type == RX_AND_TX_POWER) {
+ dump << "\tRx power - raw: (hex) " << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << rxtx_power_raw.first.first
+ << ", (dec) " << std::dec << std::setfill(' ') << std::setw(5) << rxtx_power_raw.first.first
+ << " | " << std::dec << std::fixed << std::setprecision(5) << std::setw(8) << raw_rx_to_mw(rxtx_power_raw.first.first) << " mW, "
+ << std::dec << std::setw(9) << mw_to_dbm(raw_rx_to_mw(rxtx_power_raw.first.first)) << " dBm\n";
+ }
+ if (_read_type == TX_POWER || _read_type == RX_AND_TX_POWER) {
+ dump << "\tTx power - raw: (hex) " << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << rxtx_power_raw.first.second
+ << ", (dec) " << std::dec << std::setfill(' ') << std::setw(5) << rxtx_power_raw.first.second
+ << " | " << std::dec << std::fixed << std::setprecision(5) << std::setw(8) << raw_tx_to_mw(rxtx_power_raw.first.second) << " mW, "
+ << std::dec << std::setw(9) << mw_to_dbm(raw_tx_to_mw(rxtx_power_raw.first.second)) << " dBm\n";
+ }
+ }
+
+ return dump.str();
+}
+
+int TrxEepromReader::get_buf_size() const {
+ return _buf_size;
+}
+
+int TrxEepromReader::get_read_offset() const {
+ return _read_offset;
+}
+
+int TrxEepromReader::get_read_num_bytes() const {
+ return _read_num_bytes;
+}
+
+int TrxEepromReader::get_max_ports() const {
+ return _max_ports;
+}
+
+const char* TrxEepromReader::get_node_path() const {
+ return _node_path;
+}
+
+#ifdef RXTX_POWER_EXE_MODE
+int main(int argc, char *argv[]) {
+ int port = 0;
+
+ if (argc > 1) {
+ std::string help{argv[1]};
+
+ // trim leading '-'s
+ do {
+ if (help.find_first_of("-") == std::string::npos) {
+ break;
+ } else {
+ help.erase(0, 1);
+ }
+ } while (true);
+
+ if (help == "h" || help == "help") {
+ std::cout << "usage:\n\t" << argv[0] << " " << port << '\n';
+ std::cout << "\t#1 port no\n";
+ return 0;
+ }
+
+ try {
+ port = std::stoi(argv[1]);
+ } catch(std::invalid_argument eia) {
+ std::cerr << "ERROR - invalid argument exception: '" << argv[1] << "'\n";
+ return 1;
+ } catch(std::out_of_range eoor) {
+ std::cerr << "ERROR - out of range exception: '" << argv[1] << "'\n";
+ return 1;
+ }
+ }
+
+ TrxEepromReader trx_eeprom_reader{TrxEepromReader::DEVICE_EITHER, TrxEepromReader::RX_AND_TX_POWER, port};
+
+ std::cout << trx_eeprom_reader.dump_data() << '\n';
+
+ return 0;
+}
+#endif
diff --git a/agent/src/trx_eeprom_reader.h b/agent/src/trx_eeprom_reader.h
new file mode 100644
index 0000000..25bfe99
--- /dev/null
+++ b/agent/src/trx_eeprom_reader.h
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+#ifndef TRX_EEPROM_READER_H
+#define TRX_EEPROM_READER_H
+
+/**
+ * RSSI - Received Signal Strength Indication
+ * see:
+ * https://en.wikipedia.org/wiki/Received_signal_strength_indication
+ *
+ * per EC Ticket#7825
+ * see:
+ * https://support.edge-core.com/hc/en-us/requests/7825
+ */
+
+class TrxEepromReader {
+ public:
+ enum device_type {
+ DEVICE_EITHER,
+ DEVICE_GPON,
+ DEVICE_XGSPON
+ };
+
+ enum power_type {
+ RX_POWER,
+ TX_POWER,
+ RX_AND_TX_POWER
+ };
+
+ TrxEepromReader(device_type dev_type, const power_type read_type, const int port);
+ TrxEepromReader() = delete;
+
+ static std::pair<std::string, bool> read_txt_file(const std::string& file_name);
+ static std::string get_board_name();
+
+ int read_binary_file(char* buffer);
+ bool is_valid_port() const;
+ void set_port_path();
+ unsigned long get_value_from_pointer_u(unsigned char *ptr, int size);
+ double raw_rx_to_mw(int raw);
+ double raw_tx_to_mw(int raw);
+ double mw_to_dbm(double mw);
+ std::pair<std::pair<int, int>, bool> read_power_raw();
+ std::pair<std::pair<double, double>, bool> read_power_mean_dbm();
+ std::string dump_data();
+ int get_buf_size() const;
+ int get_read_offset() const;
+ int get_read_num_bytes() const;
+ int get_max_ports() const;
+ const char* get_node_path() const;
+
+ private:
+ int _port;
+ power_type _read_type;
+ std::string _dev_name;
+ int _buf_size;
+ int _read_offset;
+ int _read_num_bytes;
+ int _port_addr;
+ std::string _name_eeprom;
+ const int* _bus_index;
+ size_t _max_ports;
+
+ char _node_format[64] = {0};
+ char _node_path[64] = {0};
+
+ const char* _master_format = "/sys/bus/i2c/devices/%s-00%d/%s";
+ const int _gpon_bus_index[74] = {
+ 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,
+ 20, 21, 25, 26, 27, 28, 29, 30,
+ 31, 32
+ };
+ const int _xgspon_bus_index[20] = {
+ 47, 48, 37, 38, 35, 36, 33, 34,
+ 39, 40, 41, 42, 43, 44, 45, 46,
+ 49, 50, 51, 52
+ };
+};
+
+#endif