From b91d7d9d78ea12097de4c518c8cd2215ff286f7a Mon Sep 17 00:00:00 2001
From: Jan Hoffmann <jan@3e8.eu>
Date: Sat, 28 Jan 2023 20:45:24 +0100
Subject: [PATCH] ltq-*-app: extend ubus metrics/statistics

Expose a few additional useful values via ubus:

- Channel error counters (CRC, FEC)
- Retransmission counters (MINEFTR, LEFTRS)
- Impulse noise protection level
- Rate adaptation mode
- OLR statistics (Bitswap, SRA, SOS)
- Pilot tones
- Upstream/downstream band information

Signed-off-by: Jan Hoffmann <jan@3e8.eu>
---
 package/network/config/ltq-adsl-app/Makefile  |   2 +-
 .../network/config/ltq-vdsl-vr11-app/Makefile |   2 +-
 .../ltq-vdsl-vr9-app/src/src/dsl_cpe_ubus.c   | 163 +++++++++++++++++-
 3 files changed, 162 insertions(+), 5 deletions(-)

diff --git a/package/network/config/ltq-adsl-app/Makefile b/package/network/config/ltq-adsl-app/Makefile
index c4ddbf7089..472e3c0ed9 100644
--- a/package/network/config/ltq-adsl-app/Makefile
+++ b/package/network/config/ltq-adsl-app/Makefile
@@ -10,7 +10,7 @@ include $(INCLUDE_DIR)/kernel.mk
 
 PKG_NAME:=dsl_cpe_control_danube
 PKG_VERSION:=3.24.4.4
-PKG_RELEASE:=10
+PKG_RELEASE:=11
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
 PKG_BUILD_DIR:=$(BUILD_DIR)/dsl_cpe_control-$(PKG_VERSION)
 PKG_SOURCE_URL:=@OPENWRT
diff --git a/package/network/config/ltq-vdsl-vr11-app/Makefile b/package/network/config/ltq-vdsl-vr11-app/Makefile
index 8639795458..bcbf41e65a 100644
--- a/package/network/config/ltq-vdsl-vr11-app/Makefile
+++ b/package/network/config/ltq-vdsl-vr11-app/Makefile
@@ -9,7 +9,7 @@ include $(INCLUDE_DIR)/kernel.mk
 
 PKG_NAME:=ltq-vdsl-vr11-app
 PKG_VERSION:=4.23.1
-PKG_RELEASE:=1
+PKG_RELEASE:=2
 PKG_BASE_NAME:=dsl_cpe_control
 
 UGW_VERSION=8.5.2.10
diff --git a/package/network/config/ltq-vdsl-vr9-app/src/src/dsl_cpe_ubus.c b/package/network/config/ltq-vdsl-vr9-app/src/src/dsl_cpe_ubus.c
index bf8a8b9c2c..77a1c67b6a 100644
--- a/package/network/config/ltq-vdsl-vr9-app/src/src/dsl_cpe_ubus.c
+++ b/package/network/config/ltq-vdsl-vr9-app/src/src/dsl_cpe_ubus.c
@@ -133,6 +133,17 @@ enum {
 	PSTATE_MAP_L3,
 };
 
+/* These values are exported via ubus and backwards compability
+ * needs to be kept!
+ */
+enum {
+	RAMODE_MAP_UNKNOWN = -1,
+	RAMODE_MAP_MANUAL,
+	RAMODE_MAP_AT_INIT,
+	RAMODE_MAP_DYNAMIC,
+	RAMODE_MAP_DYNAMIC_SOS,
+};
+
 static DSL_CPE_ThreadCtrl_t thread;
 static struct ubus_context *ctx;
 static struct blob_buf b;
@@ -171,6 +182,15 @@ static inline void m_array(const char *id, const uint8_t *value, size_t len) {
 	blobmsg_close_array(&b, c);
 }
 
+static inline void m_array_u16(const char *id, const uint16_t *value, size_t len) {
+	void *c = blobmsg_open_array(&b, id);
+
+	for (size_t i = 0; i < len; ++i)
+		blobmsg_add_u16(&b, "", value[i]);
+
+	blobmsg_close_array(&b, c);
+}
+
 static void m_vendor(const char *id, const uint8_t *value) {
 	// ITU-T T.35: U.S.
 	if (U16(value[0], value[1]) != 0xb500)
@@ -418,6 +438,31 @@ static void g997_line_inventory(int fd) {
 	m_array("serial", out.data.SerialNumber, DSL_G997_LI_MAXLEN_SERIAL);
 }
 
+static void pilot_tones_status(int fd) {
+#ifndef INCLUDE_DSL_CPE_API_DANUBE
+	IOCTL(DSL_PilotTonesStatus_t, DSL_FIO_PILOT_TONES_STATUS_GET);
+
+	m_array_u16("pilot_tones", out.data.nPilotTone, out.data.nNumData);
+#endif
+}
+
+static void band_border_status(int fd, DSL_AccessDir_t direction) {
+	IOCTL(DSL_BandBorderStatus_t, DSL_FIO_BAND_BORDER_STATUS_GET);
+
+	void *c, *c2;
+
+	c = blobmsg_open_array(&b, "limits");
+
+	for (size_t i = 0; i < out.data.nNumData; i++) {
+		c2 = blobmsg_open_table(&b, "");
+		blobmsg_add_u16(&b, "first", out.data.nBandLimits[i].nFirstToneIndex);
+		blobmsg_add_u16(&b, "last", out.data.nBandLimits[i].nLastToneIndex);
+		blobmsg_close_table(&b, c2);
+	}
+
+	blobmsg_close_array(&b, c);
+}
+
 static void g977_get_bit_allocation(int fd, DSL_AccessDir_t direction) {
 	IOCTL_DIR(DSL_G997_BitAllocationNsc_t, DSL_FIO_G997_BIT_ALLOCATION_NSC_GET, direction);
 
@@ -639,19 +684,46 @@ static void band_plan_status(int fd, profile_t *profile) {
 #endif
 }
 
-static void line_feature_config(int fd, DSL_AccessDir_t direction) {
+static void line_feature_config(int fd, DSL_AccessDir_t direction, bool *retx) {
 	IOCTL_DIR(DSL_LineFeature_t, DSL_FIO_LINE_FEATURE_STATUS_GET, direction)
 
 	m_bool("trellis", out.data.bTrellisEnable);
 	m_bool("bitswap", out.data.bBitswapEnable);
 	m_bool("retx", out.data.bReTxEnable);
 	m_bool("virtual_noise", out.data.bVirtualNoiseSupport);
+
+	*retx = out.data.bReTxEnable;
+}
+
+static void g997_rate_adaptation_status(int fd, DSL_AccessDir_t direction) {
+#ifndef INCLUDE_DSL_CPE_API_DANUBE
+	IOCTL_DIR(DSL_G997_RateAdaptationStatus_t, DSL_FIO_G997_RATE_ADAPTATION_STATUS_GET, direction);
+
+	int map = RAMODE_MAP_UNKNOWN;
+	const char *str;
+	switch (out.data.RA_MODE) {
+	STR_CASE_MAP(DSL_G997_RA_MODE_MANUAL, "Manual", RAMODE_MAP_MANUAL)
+	STR_CASE_MAP(DSL_G997_RA_MODE_AT_INIT, "At initialization", RAMODE_MAP_AT_INIT)
+	STR_CASE_MAP(DSL_G997_RA_MODE_DYNAMIC, "Dynamic", RAMODE_MAP_DYNAMIC)
+	STR_CASE_MAP(DSL_G997_RA_MODE_DYNAMIC_SOS, "Dynamic with SOS", RAMODE_MAP_DYNAMIC_SOS)
+	default:
+		str = NULL;
+		break;
+	};
+
+	if (str)
+		m_str("ra_mode", str);
+
+	if (map != PSTATE_MAP_UNKNOWN)
+		m_u32("ra_mode_num", map);
+#endif
 }
 
 static void g997_channel_status(int fd, DSL_AccessDir_t direction) {
 	IOCTL_DIR(DSL_G997_ChannelStatus_t, DSL_FIO_G997_CHANNEL_STATUS_GET, direction);
 
 	m_u32("interleave_delay", out.data.ActualInterleaveDelay * 10);
+	m_double("inp", (double)out.data.ActualImpulseNoiseProtection / 10);
 #ifndef INCLUDE_DSL_CPE_API_DANUBE
 	// prefer ACTNDR, see comments in drv_dsl_cpe_api_g997.h
 	m_u32("data_rate", out.data.ActualNetDataRate);
@@ -672,6 +744,41 @@ static void g997_line_status(int fd, DSL_AccessDir_t direction) {
 	m_u32("attndr", out.data.ATTNDR);
 }
 
+static void pm_retx_counters_showtime(int fd, DSL_XTUDir_t direction) {
+#ifdef INCLUDE_DSL_CPE_PM_RETX_COUNTERS
+	IOCTL_DIR(DSL_PM_ReTxCounters_t, DSL_FIO_PM_RETX_COUNTERS_SHOWTIME_GET, direction);
+
+	m_u32("mineftr", out.data.nEftrMin);
+#endif
+}
+
+#ifndef INCLUDE_DSL_CPE_API_DANUBE
+static void olr_statistics(int fd, DSL_AccessDir_t direction) {
+	IOCTL_DIR(DSL_OlrStatistics_t, DSL_FIO_OLR_STATISTICS_GET, direction)
+
+	void *c = blobmsg_open_table(&b, "bitswap");
+	m_u32("requested", out.data.nBitswapRequested + out.data.nBitswapRequested);
+	m_u32("executed", out.data.nBitswapExecuted);
+	m_u32("rejected", out.data.nBitswapRejected);
+	m_u32("timeout", out.data.nBitswapTimeout);
+	blobmsg_close_table(&b, c);
+
+	c = blobmsg_open_table(&b, "sra");
+	m_u32("requested", out.data.nSraRequested);
+	m_u32("executed", out.data.nSraExecuted);
+	m_u32("rejected", out.data.nSraRejected);
+	m_u32("timeout", out.data.nSraTimeout);
+	blobmsg_close_table(&b, c);
+
+	c = blobmsg_open_table(&b, "sos");
+	m_u32("requested", out.data.nSosRequested);
+	m_u32("executed", out.data.nSosExecuted);
+	m_u32("rejected", out.data.nSosRejected);
+	m_u32("timeout", out.data.nSosTimeout);
+	blobmsg_close_table(&b, c);
+}
+#endif
+
 static void pm_line_sec_counters_total(int fd, DSL_XTUDir_t direction) {
 	IOCTL_DIR(DSL_PM_LineSecCountersTotal_t, DSL_FIO_PM_LINE_SEC_COUNTERS_TOTAL_GET, direction)
 
@@ -685,6 +792,21 @@ static void pm_line_sec_counters_total(int fd, DSL_XTUDir_t direction) {
 #endif
 }
 
+static void pm_retx_counters_total(int fd, DSL_XTUDir_t direction) {
+#ifdef INCLUDE_DSL_CPE_PM_RETX_COUNTERS
+	IOCTL_DIR(DSL_PM_ReTxCountersTotal_t, DSL_FIO_PM_RETX_COUNTERS_TOTAL_GET, direction);
+
+	m_u32("leftrs", out.data.nLeftr);
+#endif
+}
+
+static void pm_channel_counters_total(int fd, DSL_XTUDir_t direction) {
+	IOCTL_DIR(DSL_PM_ChannelCountersTotal_t, DSL_FIO_PM_CHANNEL_COUNTERS_TOTAL_GET, direction);
+
+	m_u32("cv_c", out.data.nCodeViolations);
+	m_u32("fec_c", out.data.nFEC);
+}
+
 static void pm_data_path_counters_total(int fd, DSL_XTUDir_t direction) {
 	IOCTL_DIR(DSL_PM_DataPathCountersTotal_t, DSL_FIO_PM_DATA_PATH_COUNTERS_TOTAL_GET, direction);
 
@@ -810,6 +932,17 @@ static int line_statistics(struct ubus_context *ctx, struct ubus_object *obj,
 
 	blob_buf_init(&b, 0);
 
+	pilot_tones_status(fd);
+
+	c = blobmsg_open_table(&b, "bands");
+	c2 = blobmsg_open_table(&b, "downstream");
+	band_border_status(fd, DSL_DOWNSTREAM);
+	blobmsg_close_table(&b, c2);
+	c2 = blobmsg_open_table(&b, "upstream");
+	band_border_status(fd, DSL_UPSTREAM);
+	blobmsg_close_table(&b, c2);
+	blobmsg_close_table(&b, c);
+
 	c = blobmsg_open_table(&b, "bits");
 	c2 = blobmsg_open_table(&b, "downstream");
 	g977_get_bit_allocation(fd, DSL_DOWNSTREAM);
@@ -862,6 +995,7 @@ static int metrics(struct ubus_context *ctx, struct ubus_object *obj,
 	standard_t standard = STD_UNKNOWN;
 	profile_t profile = PROFILE_UNKNOWN;
 	vector_t vector = VECTOR_UNKNOWN;
+	bool retx_up = false, retx_down = false;
 
 #ifndef INCLUDE_DSL_CPE_API_DANUBE
 	fd = open(DSL_CPE_DEVICE_NAME "/0", O_RDWR, 0644);
@@ -908,9 +1042,12 @@ static int metrics(struct ubus_context *ctx, struct ubus_object *obj,
 	default:
 		break;
 	};
-	line_feature_config(fd, DSL_UPSTREAM);
+	line_feature_config(fd, DSL_UPSTREAM, &retx_up);
+	g997_rate_adaptation_status(fd, DSL_UPSTREAM);
 	g997_channel_status(fd, DSL_UPSTREAM);
 	g997_line_status(fd, DSL_UPSTREAM);
+	if (retx_up)
+		pm_retx_counters_showtime(fd, DSL_FAR_END);
 	blobmsg_close_table(&b, c);
 
 	c = blobmsg_open_table(&b, "downstream");
@@ -925,20 +1062,40 @@ static int metrics(struct ubus_context *ctx, struct ubus_object *obj,
 	default:
 		break;
 	};
-	line_feature_config(fd, DSL_DOWNSTREAM);
+	line_feature_config(fd, DSL_DOWNSTREAM, &retx_down);
+	g997_rate_adaptation_status(fd, DSL_DOWNSTREAM);
 	g997_channel_status(fd, DSL_DOWNSTREAM);
 	g997_line_status(fd, DSL_DOWNSTREAM);
+	if (retx_down)
+		pm_retx_counters_showtime(fd, DSL_NEAR_END);
+	blobmsg_close_table(&b, c);
+
+#ifndef INCLUDE_DSL_CPE_API_DANUBE
+	c = blobmsg_open_table(&b, "olr");
+	c2 = blobmsg_open_table(&b, "downstream");
+	olr_statistics(fd, DSL_DOWNSTREAM);
+	blobmsg_close_table(&b, c2);
+	c2 = blobmsg_open_table(&b, "upstream");
+	olr_statistics(fd, DSL_UPSTREAM);
+	blobmsg_close_table(&b, c2);
 	blobmsg_close_table(&b, c);
+#endif
 
 	c = blobmsg_open_table(&b, "errors");
 	c2 = blobmsg_open_table(&b, "near");
 	pm_line_sec_counters_total(fd, DSL_NEAR_END);
+	if (retx_down)
+		pm_retx_counters_total(fd, DSL_NEAR_END);
+	pm_channel_counters_total(fd, DSL_NEAR_END);
 	pm_data_path_counters_total(fd, DSL_NEAR_END);
 	retx_statistics(fd, DSL_NEAR_END);
 	blobmsg_close_table(&b, c2);
 
 	c2 = blobmsg_open_table(&b, "far");
 	pm_line_sec_counters_total(fd, DSL_FAR_END);
+	if (retx_up)
+		pm_retx_counters_total(fd, DSL_FAR_END);
+	pm_channel_counters_total(fd, DSL_FAR_END);
 	pm_data_path_counters_total(fd, DSL_FAR_END);
 	retx_statistics(fd, DSL_FAR_END);
 	blobmsg_close_table(&b, c2);
-- 
2.30.2