diff --git a/cmd_parser.c b/cmd_parser.c
index d65e179..6c91ad2 100644
--- a/cmd_parser.c
+++ b/cmd_parser.c
@@ -457,6 +457,21 @@ void parse_port(void)
}
+void sfp_print_measurements(uint8_t sfp)
+{
+ print_string("Options: "); print_byte(sfp_read_reg(sfp, 92)); write_char('\n');
+ if (!(sfp_read_reg(sfp, 92) & 0x40))
+ return;
+ print_string("Temp: "); print_byte(sfp_read_reg(sfp, 224)); print_byte(sfp_read_reg(sfp, 225)); write_char('\n');
+ print_string("Vcc: "); print_byte(sfp_read_reg(sfp, 226)); print_byte(sfp_read_reg(sfp, 227)); write_char('\n');
+ print_string("TX Bias: "); print_byte(sfp_read_reg(sfp, 228)); print_byte(sfp_read_reg(sfp, 229)); write_char('\n');
+ print_string("TX Power: "); print_byte(sfp_read_reg(sfp, 230)); print_byte(sfp_read_reg(sfp, 231)); write_char('\n');
+ print_string("RX Power: "); print_byte(sfp_read_reg(sfp, 232)); print_byte(sfp_read_reg(sfp, 233)); write_char('\n');
+ print_string("Laser: "); print_byte(sfp_read_reg(sfp, 234)); print_byte(sfp_read_reg(sfp, 235)); write_char('\n');
+ print_string("State: "); print_byte(sfp_read_reg(sfp, 238)); write_char('\n');
+}
+
+
void parse_regget(void)
{
uint16_t reg = 0;
@@ -662,11 +677,13 @@ void cmd_parser(void) __banked
print_string(" Encoding: "); print_byte(sfp_read_reg(0, 11));
print_string("\n");
sfp_print_info(0);
+ sfp_print_measurements(0);
if (machine.n_sfp == 2) {
print_string("\nSlot 2 - Rate: "); print_byte(sfp_read_reg(1, 12));
print_string(" Encoding: "); print_byte(sfp_read_reg(1, 11));
print_string("\n");
sfp_print_info(1);
+ sfp_print_measurements(1);
}
} else if (cmd_compare(0, "stat")) {
port_stats_print();
diff --git a/html/eee.html b/html/eee.html
index d7c78c8..e5ec472 100644
--- a/html/eee.html
+++ b/html/eee.html
@@ -1,6 +1,5 @@
-
EEE Configuration
diff --git a/html/index.html b/html/index.html
index 765f065..04532db 100644
--- a/html/index.html
+++ b/html/index.html
@@ -1,6 +1,5 @@
-
diff --git a/html/lag.html b/html/lag.html
index 85c6dff..29b2b66 100644
--- a/html/lag.html
+++ b/html/lag.html
@@ -1,6 +1,5 @@
-
Link Aggregation Configuration
diff --git a/html/main.js b/html/main.js
index 5d5246e..5a7473d 100644
--- a/html/main.js
+++ b/html/main.js
@@ -6,6 +6,36 @@ const linkS = ["Disabled", "No Link", "100M", "1000M", "500M", "10G", "2.5G", "5
var pState = new Int8Array(10);
var pIsSFP = new Int8Array(10);
var numPorts = 0;
+const logToPhysPort = [0, 0, 0, 5, 1, 2, 3, 4, 6];
+const physToLogPort = [ 4, 5, 6, 7, 3, 8];
+function drawPorts() {
+ var f = document.getElementById('ports');
+ console.log("DRAWING PORTS: ", numPorts);
+ for (let i = 0; i < numPorts; i++) {
+ console.log("DRAWING isSFP: ", pIsSFP[i]);
+ const d = document.createElement("div");
+ d.classList.add('tooltip');
+ const s = document.createElement("span");
+ s.classList.add("tooltiptext");
+ s.innerHTML = "Tooltip text";
+ s.id="tt_" + (i+1);
+ const l = document.createElement("object");
+ d.appendChild(l);
+ d.appendChild(s);
+ l.type = "image/svg+xml";
+ if (!pIsSFP[i]) {
+ l.data = "port.svg";
+ l.width ="40";
+ l.height ="40";
+ } else {
+ l.data = "sfp.svg";
+ l.width = "60";
+ l.height = "60";
+ }
+ l.id="port" + (i+1);
+ f.appendChild(d);
+ }
+}
function update() {
var xhttp = new XMLHttpRequest();
@@ -25,9 +55,11 @@ function update() {
p = s[i];
let n = p.portNum;
let pid = "port" + n;
+ let ttid = "tt_" + n;
n--;
txG[n] = BigInt(p.txG); txB[n] = BigInt(p.txB); rxG[n] = BigInt(p.rxG); rxB[n] = BigInt(p.rxB);
var psvg = document.getElementById(pid);
+ var tt = document.getElementById(ttid);
if (psvg == null || !psvg.contentDocument)
continue;
var bgs = psvg.contentDocument.getElementsByClassName("bg");
@@ -37,6 +69,7 @@ function update() {
bgs[0].style.fill = "red";
leds[0].style.fill = "black"; leds[1].style.fill = "black";
psvg.style.opacity = 0.4;
+ tt.innerHTML = "Not enabled.";
} else {
psvg.style.opacity = 1.0;
pState[n] = p.link;
@@ -48,6 +81,22 @@ function update() {
leds[0].style.fill = "black"; leds[1].style.fill = "black";
psvg.style.opacity = 0.4
}
+ var iHTML = "";
+ iHTML += "| Link speed | : | " + linkS[p.link + 1] + " |
";
+ if (p.isSFP) {
+ iHTML += "| Vendor | : | " + p.sfp_vendor + " |
";
+ iHTML += "| Model | : | " + p.sfp_model + " |
";
+ iHTML += "| Serial | : | " + p.sfp_serial + " |
";
+ if (p.sfp_options & 0x40) {
+ iHTML += "| Temp | : | " + (Number(p.sfp_temp) >> 8) + "." + ((Number(p.sfp_temp) & 0xff)/256.0 * 100).toFixed(0) + " ℃ |
";
+ iHTML += "| Vcc | : | " + (Number(p.sfp_vcc) / 10000.0).toFixed(2) + " V |
";
+ iHTML += "| TX-Bias | : | " + (Number(p.sfp_txbias) / 500.0).toFixed(1) + " mA |
";
+ iHTML += "| TX-Power | : | " + (Number(p.sfp_txpower) / 10.0).toFixed(0) + " mW |
";
+ iHTML += "| RX-Power | : | " + (Number(p.sfp_rxpower) / 10.0).toFixed(0) + " mW |
";
+ }
+ }
+ iHTML += "
";
+ tt.innerHTML = iHTML;
}
}
}
diff --git a/html/mirror.html b/html/mirror.html
index 0b8ea8e..edf5b77 100644
--- a/html/mirror.html
+++ b/html/mirror.html
@@ -1,6 +1,5 @@
-
Mirror Configuration
diff --git a/html/ports.js b/html/ports.js
deleted file mode 100644
index 0a2fe2a..0000000
--- a/html/ports.js
+++ /dev/null
@@ -1,24 +0,0 @@
-const logToPhysPort = [0, 0, 0, 5, 1, 2, 3, 4, 6];
-const physToLogPort = [ 4, 5, 6, 7, 3, 8];
-
-function drawPorts() {
- var f = document.getElementById('ports');
- console.log("DRAWING PORTS: ", numPorts);
- for (let i = 0; i < numPorts; i++) {
- console.log("DRAWING isSFP: ", pIsSFP[i]);
- const l = document.createElement("object");
- l.type = "image/svg+xml";
- if (!pIsSFP[i]) {
- l.data = "port.svg";
- l.width ="40";
- l.height ="40";
- } else {
- l.data = "sfp.svg";
- l.width = "60";
- l.height = "60";
- }
- l.id="port" + (i+1);
- f.appendChild(l);
- }
- console.log("DRAWING DONE ");
-}
diff --git a/html/stat.html b/html/stat.html
index 467f0bf..5cfaf05 100644
--- a/html/stat.html
+++ b/html/stat.html
@@ -1,6 +1,5 @@
-
FreeSwitchOS Port Statistics
diff --git a/html/style.css b/html/style.css
index a4edf4d..477cca1 100644
--- a/html/style.css
+++ b/html/style.css
@@ -26,7 +26,7 @@ li a:hover {
}
table, th, td {
- border: 1px solid navy;
+ border: 1px solid #226;
padding: 8px;
}
table {
@@ -84,7 +84,6 @@ object {
.row {display: flex;}
.rcol {flex: 90%;}
.lcol {flex: 10%;}
-span::before{ content:''; position: absolute; top: 40px; left:0; width: 100%; height: 2px; background: #aaf;}
/* Login page */
.login_page {margin: 0; padding: 0; background: #aaf; height: 100vh; overflow: hidden;}
@@ -118,3 +117,46 @@ margin: 30px 0;
.txt_field span::before{ content:''; position: absolute; top: 40px; left:0; width: 100%; height: 2px; background: #aaf;}
.txt_field input:focus ~ label,
.txt_field input:valid ~ label { top: -5px; color: #aaf; }
+
+.tooltip {
+ position: relative; display: inline-block; cursor: pointer;
+}
+
+
+.tooltiptext {
+ visibility: hidden;
+ width: 260px;
+ background-color: #226;
+ color: #fff;
+ text-align: center;
+ border-radius: 6px;
+ padding: 5px 0;
+ position: absolute;
+ z-index: 1;
+ top: 80%;
+ left: 50%;
+ margin-left: -130px;
+}
+
+.tooltiptext::after {
+ content: "";
+ position: absolute;
+ bottom: 100%;
+ left: 50%;
+ margin-left: -5px;
+ border-width: 5px;
+ border-style: solid;
+ border-color: transparent transparent black transparent;
+}
+
+
+.tooltip:hover .tooltiptext {
+ visibility: visible;
+ opacity: 1;
+}
+.tt_table {
+ width: 100%; font-size:90%;
+}
+.tt_table td {
+ text-align: left;
+}
diff --git a/html/vlan.html b/html/vlan.html
index 8bc66f9..a3f91e0 100644
--- a/html/vlan.html
+++ b/html/vlan.html
@@ -1,6 +1,5 @@
-
FreeSwitchOS VLAN Configuration
diff --git a/httpd/httpd.c b/httpd/httpd.c
index 8f6c923..fa9b4cb 100644
--- a/httpd/httpd.c
+++ b/httpd/httpd.c
@@ -593,7 +593,7 @@ void httpd_appcall(void)
slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nContent-Type: ");
slen += strtox(outbuf + slen, mime_strings[f_data[entry].mime]);
- slen += strtox(outbuf + slen, "\r\nCache-Control: max-age=60, must-revalidate\r\n\r\n");
+ slen += strtox(outbuf + slen, "; charset=UTF-8\r\nCache-Control: max-age=60, must-revalidate\r\n\r\n");
len_left = f_data[entry].len;
if (len_left > (TCP_OUTBUF_SIZE - slen)) {
cont_len = len_left - (TCP_OUTBUF_SIZE - slen);
diff --git a/httpd/page_impl.c b/httpd/page_impl.c
index 0b97815..6a64760 100644
--- a/httpd/page_impl.c
+++ b/httpd/page_impl.c
@@ -34,6 +34,11 @@ extern __xdata uint16_t cmd_history_ptr;
extern __xdata struct flash_region_t flash_region;
+extern __xdata char sfp_module_vendor[2][17];
+extern __xdata char sfp_module_model[2][17];
+extern __xdata char sfp_module_serial[2][17];
+extern __xdata uint8_t sfp_options[2];
+
__code uint8_t * __code HTTP_RESPONCE_JSON = "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n";
__code uint8_t * __code HTTP_RESPONCE_TXT = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n";
@@ -148,6 +153,40 @@ void send_sfp_info(uint8_t sfp)
}
+void sfp_send_data(uint8_t slot, uint8_t reg, uint8_t len)
+{
+ if (reg & 0x80) { // Configure SFP readings address (0x51) as I2C device address
+ reg &= 0x7f;
+ REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | len & 0xf, 0x51 >> 5, (0x51 << 3) & 0xff);
+ } else {
+ REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | len & 0xf, 0x50 >> 5, (0x50 << 3) & 0xff);
+ }
+
+ reg_read_m(RTL837X_REG_I2C_CTRL);
+ sfr_mask_data(1, 0xfc, machine.sfp_port[slot].i2c == 0 ? SCL_PIN << 5 | SDA_PIN_0 << 2 : SCL_PIN << 5 | SDA_PIN_1 << 2 );
+ reg_write_m(RTL837X_REG_I2C_CTRL);
+
+ REG_WRITE(RTL837X_REG_I2C_IN, 0, 0, 0, reg);
+
+ // Execute I2C Read
+ reg_bit_set(RTL837X_REG_I2C_CTRL, 0);
+
+ // Wait for execution to finish
+ do {
+ reg_read_m(RTL837X_REG_I2C_CTRL);
+ } while (sfr_data[3] & 0x1);
+
+ for (uint8_t i = 0; i < len & 0xf; i++) {
+ if (!(i & 0x3))
+ reg_read_m(RTL837X_REG_I2C_OUT + (i >> 2));
+ if (len & 0x80)
+ char_to_html(sfr_data[3 - (i & 0x3)]);
+ else
+ byte_to_html(sfr_data[3 - (i & 0x3)]);
+ }
+}
+
+
void send_basic_info(void)
{
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
@@ -354,7 +393,38 @@ void send_status(void)
if (machine.is_sfp[i]) {
slen += strtox(outbuf + slen, ",\"isSFP\":1,\"enabled\":");
- bool_to_html(!(sfp_pins_last & (0x1 << ((machine.is_sfp[i] - 1) << 2))));
+ if (!(sfp_pins_last & (0x1 << ((machine.is_sfp[i] - 1) << 2)))) {
+ bool_to_html(1);
+ slen += strtox(outbuf + slen,",\"sfp_options\":\"0x");
+ byte_to_html(sfp_options[machine.is_sfp[i]-1]);
+ if (sfp_options[machine.is_sfp[i]-1] & 0x40) {
+ sfp_send_data(machine.is_sfp[i] - 1, 92, 1);
+ slen += strtox(outbuf + slen,"\",\"sfp_temp\":\"0x");
+ sfp_send_data(machine.is_sfp[i] - 1, 224, 2);
+ slen += strtox(outbuf + slen,"\",\"sfp_vcc\":\"0x");
+ sfp_send_data(machine.is_sfp[i] - 1, 226, 2);
+ slen += strtox(outbuf + slen,"\",\"sfp_txbias\":\"0x");
+ sfp_send_data(machine.is_sfp[i] - 1, 228, 2);
+ slen += strtox(outbuf + slen,"\",\"sfp_txpower\":\"0x");
+ sfp_send_data(machine.is_sfp[i] - 1, 230, 2);
+ slen += strtox(outbuf + slen,"\",\"sfp_rxpower\":\"0x");
+ sfp_send_data(machine.is_sfp[i] - 1, 232, 2);
+ slen += strtox(outbuf + slen,"\",\"sfp_state\":\"0x");
+ sfp_send_data(machine.is_sfp[i] - 1, 238, 1);
+ }
+ slen += strtox(outbuf + slen,"\",\"sfp_vendor\":\"");
+ for (register uint8_t s = 0; s < 16; s++)
+ outbuf[slen++] = sfp_module_vendor[machine.is_sfp[i]-1][s];
+ slen += strtox(outbuf + slen,"\",\"sfp_model\":\"");
+ for (register uint8_t s = 0; s < 16; s++)
+ outbuf[slen++] = sfp_module_model[machine.is_sfp[i]-1][s];
+ slen += strtox(outbuf + slen,"\",\"sfp_serial\":\"");
+ for (register uint8_t s = 0; s < 16; s++)
+ outbuf[slen++] = sfp_module_serial[machine.is_sfp[i]-1][s];
+ char_to_html('"');
+ } else {
+ bool_to_html(0);
+ }
} else {
slen += strtox(outbuf + slen, ",\"isSFP\":0,\"enabled\":");
phy_read(i, 0x1f, 0xa610);
diff --git a/rtl837x_common.h b/rtl837x_common.h
index 7bf1d9f..ef8d4d6 100644
--- a/rtl837x_common.h
+++ b/rtl837x_common.h
@@ -101,6 +101,7 @@ void print_reg(uint16_t reg);
uint8_t sfp_read_reg(uint8_t slot, uint8_t reg);
void reg_bit_set(uint16_t reg_addr, char bit);
void reg_bit_clear(uint16_t reg_addr, char bit);
+void sfr_mask_data(uint8_t n, uint8_t mask, uint8_t set);
void sfr_set_zero(void);
void reset_chip(void);
void memcpy(__xdata void * __xdata dst, __xdata const void * __xdata src, uint16_t len);
diff --git a/rtl837x_phy.c b/rtl837x_phy.c
index 1c8c9e6..20fd80e 100644
--- a/rtl837x_phy.c
+++ b/rtl837x_phy.c
@@ -348,7 +348,6 @@ void phy_show(uint8_t port) __banked
break;
default:
print_string("Unknown\n");
- break;
}
break;
default:
diff --git a/rtlplayground.c b/rtlplayground.c
index 5f81c54..7beb6b3 100644
--- a/rtlplayground.c
+++ b/rtlplayground.c
@@ -101,7 +101,10 @@ __xdata uint8_t was_offline;
__xdata uint8_t linkbits_last[4];
__xdata uint8_t linkbits_last_p89;
__xdata uint8_t sfp_pins_last;
-
+__xdata char sfp_module_vendor[2][17];
+__xdata char sfp_module_model[2][17];
+__xdata char sfp_module_serial[2][17];
+__xdata uint8_t sfp_options[2];
#define ETHERTYPE_OFFSET (12 + VLAN_TAG_SIZE + RTL_TAG_SIZE)
@@ -745,16 +748,17 @@ void sds_config(uint8_t sds, uint8_t mode)
*/
uint8_t sfp_read_reg(uint8_t slot, uint8_t reg)
{
- if (machine.sfp_port[slot].i2c == 0) {
- reg_read_m(RTL837X_REG_I2C_CTRL);
- sfr_mask_data(1, 0xfc, SCL_PIN << 5 | SDA_PIN_0 << 2);
- reg_write_m(RTL837X_REG_I2C_CTRL);
+ if (reg & 0x80) { // Configure SFP readings address (0x51) as I2C device address
+ reg &= 0x7f;
+ REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | 1, 0x51 >> 5, (0x51 << 3) & 0xff);
} else {
- reg_read_m(RTL837X_REG_I2C_CTRL);
- sfr_mask_data(1, 0xfc, SCL_PIN << 5 | SDA_PIN_1 << 2);
- reg_write_m(RTL837X_REG_I2C_CTRL);
+ REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | 1, 0x50 >> 5, (0x50 << 3) & 0xff);
}
+ reg_read_m(RTL837X_REG_I2C_CTRL);
+ sfr_mask_data(1, 0xfc, machine.sfp_port[slot].i2c == 0 ? SCL_PIN << 5 | SDA_PIN_0 << 2 : SCL_PIN << 5 | SDA_PIN_1 << 2 );
+ reg_write_m(RTL837X_REG_I2C_CTRL);
+
REG_WRITE(RTL837X_REG_I2C_IN, 0, 0, 0, reg);
// Execute I2C Read
@@ -922,6 +926,20 @@ void sfp_print_info(uint8_t sfp)
}
+void sfp_get_info(uint8_t sfp)
+{
+ for (uint8_t i = 20; i < 36; i++)
+ sfp_module_vendor[sfp][i-20] = sfp_read_reg(sfp, i);
+ sfp_module_vendor[sfp][16] = '\0';
+ for (uint8_t i = 40; i < 56; i++)
+ sfp_module_model[sfp][i-40] = sfp_read_reg(sfp, i);
+ sfp_module_model[sfp][16] = '\0';
+ for (uint8_t i = 68; i < 84; i++)
+ sfp_module_serial[sfp][i-68] = sfp_read_reg(sfp, i);
+ sfp_module_serial[sfp][16] = '\0';
+}
+
+
bool gpio_pin_test(uint8_t pin)
{
reg_read_m(RTL837X_REG_GPIO_00_31_INPUT + (pin > 31 ? 4 : 0));
@@ -944,6 +962,8 @@ void handle_sfp(void)
print_string(" Encoding: "); print_byte(sfp_read_reg(sfp, 11));
print_string(" Module: "); sfp_print_info(sfp);
print_string("\n");
+ sfp_options[sfp] = sfp_read_reg(sfp, 92);
+ sfp_get_info(sfp);
sds_config(machine.sfp_port[sfp].sds, sfp_rate_to_sds_config(rate));
}
} else {
@@ -1646,7 +1666,9 @@ void setup_i2c(void)
{
REG_SET(RTL837X_REG_I2C_MST_IF_CTRL, 0);
// Configure SFP EEPROM address (0x50) as I2C device address
- REG_SET(RTL837X_REG_I2C_CTRL, 0x1L << I2C_MEM_ADDR_WIDTH | 0x50 << I2C_DEV_ADDR);
+ // Configure SFP readings address (0x51) as I2C device address
+ REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16), 0x50 >> 5, (0x50 << 3) & 0xff);
+
REG_SET(RTL837X_REG_I2C_CTRL2, 0);
// HW Control register, enable I2C?
diff --git a/tools/httpd_sim.c b/tools/httpd_sim.c
index 3463d23..625894d 100644
--- a/tools/httpd_sim.c
+++ b/tools/httpd_sim.c
@@ -21,6 +21,7 @@ time_t last_session_use;
uint64_t txG[PORTS], txB[PORTS], rxG[PORTS], rxB[PORTS];
char txG_buff[20], txB_buff[20], rxG_buff[20], rxB_buff[20];
+char sfp_temp[8], sfp_vcc[8], sfp_txbias[8], sfp_txpower[8], sfp_rxpower[8], sfp_laser[8], sfp_options[6];
char num_buff[20];
char upload_buffer[4194304]; // 4MB
@@ -142,6 +143,33 @@ void send_status(int s)
json_object_object_add(v, "txB", json_object_new_string(txB_buff));
json_object_object_add(v, "rxG", json_object_new_string(rxG_buff));
json_object_object_add(v, "rxB", json_object_new_string(rxB_buff));
+ if (i >= 5) {
+ uint16_t temp = 0x28fb + rand() / (RAND_MAX / 100);
+ uint16_t vcc = 0x7eda + rand() / (RAND_MAX / 100);
+ uint16_t txbias = 0x0d24 +rand() / (RAND_MAX / 100);
+ uint16_t txpower = 0x14bd + rand() / (RAND_MAX / 100);
+ uint16_t rxpower = 0;
+ uint16_t laser = 0;
+ uint16_t options = 0x68;
+
+ sprintf(sfp_options, "0x%02x", options);
+ sprintf(sfp_temp, "0x%04x", temp);
+ sprintf(sfp_vcc, "0x%04x", vcc);
+ sprintf(sfp_txbias, "0x%04x", txbias);
+ sprintf(sfp_txpower, "0x%04x", txpower);
+ sprintf(sfp_rxpower, "0x%04x", rxpower);
+ sprintf(sfp_laser, "0x%04x", laser);
+ json_object_object_add(v, "sfp_vendor", json_object_new_string("OEM"));
+ json_object_object_add(v, "sfp_model", json_object_new_string("10G-SFP+"));
+ json_object_object_add(v, "sfp_serial", json_object_new_string("12345678"));
+ json_object_object_add(v, "sfp_options", json_object_new_string(sfp_options));
+ json_object_object_add(v, "sfp_temp", json_object_new_string(sfp_temp));
+ json_object_object_add(v, "sfp_vcc", json_object_new_string(sfp_vcc));
+ json_object_object_add(v, "sfp_txbias", json_object_new_string(sfp_txbias));
+ json_object_object_add(v, "sfp_txpower", json_object_new_string(sfp_txpower));
+ json_object_object_add(v, "sfp_rxpower", json_object_new_string(sfp_rxpower));
+ json_object_object_add(v, "sfp_laser", json_object_new_string(sfp_laser));
+ }
json_object_array_add(ports, v);
}
last_called = now;