bf8a8b9c2c0d9c771b0877d0b6f323a922fb7a87
[openwrt/staging/noltari.git] / package / network / config / ltq-vdsl-vr9-app / src / src / dsl_cpe_ubus.c
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3 * Copyright (C) 2020 Andre Heider <a.heider@gmail.com>
4 */
5
6 #include <sys/ioctl.h>
7 #include <sys/stat.h>
8 #include <sys/types.h>
9 #include <fcntl.h>
10 #include <libubus.h>
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <unistd.h>
15
16 #include "dsl_cpe_control.h"
17 #include <drv_dsl_cpe_api_ioctl.h>
18 #ifdef INCLUDE_DSL_CPE_API_VRX
19 #include <drv_mei_cpe_interface.h>
20 #endif
21
22 #define U16(v1, v2) ( \
23 ((uint16_t)(v1) << 8) | \
24 ((uint16_t)(v2)))
25
26 #define U32(v1, v2, v3, v4) ( \
27 ((uint32_t)(v1) << 24) | \
28 ((uint32_t)(v2) << 16) | \
29 ((uint32_t)(v3) << 8) | \
30 ((uint32_t)(v4)))
31
32 #define STR_CASE(id, text) \
33 case id: \
34 str = text; \
35 break;
36
37 #define STR_CASE_MAP(id, text, number) \
38 case id: \
39 str = text; \
40 map = number; \
41 break;
42
43 #define IOCTL(type, request) \
44 type out; \
45 memset(&out, 0, sizeof(type)); \
46 if (ioctl(fd, request, &out)) \
47 return;
48
49 #define IOCTL_DIR(type, request, dir) \
50 type out; \
51 memset(&out, 0, sizeof(type)); \
52 out.nDirection = dir; \
53 if (ioctl(fd, request, &out)) \
54 return;
55
56 #define IOCTL_DIR_DELT(type, request, dir, delt) \
57 type out; \
58 memset(&out, 0, sizeof(type)); \
59 out.nDirection = dir; \
60 out.nDeltDataType = delt; \
61 if (ioctl(fd, request, &out)) \
62 return;
63
64 typedef enum {
65 ANNEX_UNKNOWN = 0,
66 ANNEX_A,
67 ANNEX_B,
68 ANNEX_C,
69 ANNEX_I,
70 ANNEX_J,
71 ANNEX_L,
72 ANNEX_M,
73 } annex_t;
74
75 typedef enum {
76 STD_UNKNOWN = 0,
77 STD_T1_413,
78 STD_TS_101_388,
79 STD_G_992_1,
80 STD_G_992_2,
81 STD_G_992_3,
82 STD_G_992_4,
83 STD_G_992_5,
84 STD_G_993_1,
85 STD_G_993_2,
86 } standard_t;
87
88 typedef enum {
89 VECTOR_UNKNOWN = 0,
90 VECTOR_OFF,
91 VECTOR_ON_DS,
92 VECTOR_ON_DS_US,
93 } vector_t;
94
95 typedef enum {
96 PROFILE_UNKNOWN = 0,
97 PROFILE_8A,
98 PROFILE_8B,
99 PROFILE_8C,
100 PROFILE_8D,
101 PROFILE_12A,
102 PROFILE_12B,
103 PROFILE_17A,
104 PROFILE_30A,
105 PROFILE_35B,
106 } profile_t;
107
108 /* These values are exported via ubus and backwards compability
109 * needs to be kept!
110 */
111 enum {
112 LSTATE_MAP_UNKNOWN = -1,
113 LSTATE_MAP_NOT_INITIALIZED,
114 LSTATE_MAP_EXCEPTION,
115 LSTATE_MAP_IDLE,
116 LSTATE_MAP_SILENT,
117 LSTATE_MAP_HANDSHAKE,
118 LSTATE_MAP_FULL_INIT,
119 LSTATE_MAP_SHOWTIME_NO_SYNC,
120 LSTATE_MAP_SHOWTIME_TC_SYNC,
121 LSTATE_MAP_RESYNC,
122 };
123
124 /* These values are exported via ubus and backwards compability
125 * needs to be kept!
126 */
127 enum {
128 PSTATE_MAP_UNKNOWN = -2,
129 PSTATE_MAP_NA,
130 PSTATE_MAP_L0,
131 PSTATE_MAP_L1,
132 PSTATE_MAP_L2,
133 PSTATE_MAP_L3,
134 };
135
136 static DSL_CPE_ThreadCtrl_t thread;
137 static struct ubus_context *ctx;
138 static struct blob_buf b;
139
140 static inline void m_null() {
141 blobmsg_add_field(&b, BLOBMSG_TYPE_UNSPEC, "", NULL, 0);
142 }
143
144 static inline void m_double(const char *id, double value) {
145 blobmsg_add_double(&b, id, value);
146 }
147
148 static inline void m_bool(const char *id, bool value) {
149 blobmsg_add_u8(&b, id, value);
150 }
151
152 static inline void m_u32(const char *id, uint32_t value) {
153 blobmsg_add_u64(&b, id, value);
154 }
155
156 static inline void m_str(const char *id, const char *value) {
157 blobmsg_add_string(&b, id, value);
158 }
159
160 static inline void m_db(const char *id, int value, int invalid) {
161 if (value != invalid)
162 m_double(id, (double)value / 10);
163 }
164
165 static inline void m_array(const char *id, const uint8_t *value, size_t len) {
166 void *c = blobmsg_open_array(&b, id);
167
168 for (size_t i = 0; i < len; ++i)
169 blobmsg_add_u16(&b, "", value[i]);
170
171 blobmsg_close_array(&b, c);
172 }
173
174 static void m_vendor(const char *id, const uint8_t *value) {
175 // ITU-T T.35: U.S.
176 if (U16(value[0], value[1]) != 0xb500)
177 return;
178
179 const char *str = NULL;
180 switch (U32(value[2], value[3], value[4], value[5])) {
181 STR_CASE(0x414C4342, "Alcatel")
182 STR_CASE(0x414E4456, "Analog Devices")
183 STR_CASE(0x4244434D, "Broadcom")
184 STR_CASE(0x43454E54, "Centillium")
185 STR_CASE(0x4753504E, "Globespan")
186 STR_CASE(0x494B4E53, "Ikanos")
187 STR_CASE(0x4946544E, "Infineon")
188 STR_CASE(0x54535443, "Texas Instruments")
189 STR_CASE(0x544D4D42, "Thomson MultiMedia Broadband")
190 STR_CASE(0x5443544E, "Trend Chip Technologies")
191 STR_CASE(0x53544D49, "ST Micro")
192 };
193
194 if (!str)
195 return;
196
197 if ((value[6] == 0) && (value[7] == 0)) {
198 m_str(id, str);
199 return;
200 }
201
202 char buf[64];
203 sprintf(buf, "%s %d.%d", str, value[6], value[7]);
204 m_str(id, buf);
205
206 return;
207 }
208
209 annex_t get_annex(const uint8_t *xtse) {
210 if ((xtse[0] & XTSE_1_01_A_T1_413) ||
211 (xtse[0] & XTSE_1_03_A_1_NO) ||
212 (xtse[0] & XTSE_1_04_A_1_O) ||
213 (xtse[1] & XTSE_2_01_A_2_NO) ||
214 (xtse[2] & XTSE_3_03_A_3_NO) ||
215 (xtse[2] & XTSE_3_04_A_3_O) ||
216 (xtse[3] & XTSE_4_01_A_4_NO) ||
217 (xtse[3] & XTSE_4_02_A_4_O) ||
218 (xtse[5] & XTSE_6_01_A_5_NO) ||
219 (xtse[5] & XTSE_6_02_A_5_O) ||
220 (xtse[7] & XTSE_8_01_A))
221 return ANNEX_A;
222
223 if ((xtse[0] & XTSE_1_05_B_1_NO) ||
224 (xtse[0] & XTSE_1_06_B_1_O) ||
225 (xtse[1] & XTSE_2_02_B_2_O) ||
226 (xtse[2] & XTSE_3_05_B_3_NO) ||
227 (xtse[2] & XTSE_3_06_B_3_O) ||
228 (xtse[5] & XTSE_6_03_B_5_NO) ||
229 (xtse[5] & XTSE_6_04_B_5_O) ||
230 (xtse[7] & XTSE_8_02_B))
231 return ANNEX_B;
232
233 if ((xtse[0] & XTSE_1_02_C_TS_101388) ||
234 (xtse[0] & XTSE_1_07_C_1_NO) ||
235 (xtse[0] & XTSE_1_08_C_1_O) ||
236 (xtse[1] & XTSE_2_03_C_2_NO) ||
237 (xtse[1] & XTSE_2_04_C_2_O) ||
238 (xtse[7] & XTSE_8_03_C))
239 return ANNEX_C;
240
241 if ((xtse[3] & XTSE_4_05_I_3_NO) ||
242 (xtse[3] & XTSE_4_06_I_3_O) ||
243 (xtse[4] & XTSE_5_01_I_4_NO) ||
244 (xtse[4] & XTSE_5_02_I_4_O) ||
245 (xtse[5] & XTSE_6_07_I_5_NO) ||
246 (xtse[5] & XTSE_6_08_I_5_O))
247 return ANNEX_I;
248
249 if ((xtse[3] & XTSE_4_07_J_3_NO) ||
250 (xtse[3] & XTSE_4_08_J_3_O) ||
251 (xtse[6] & XTSE_7_01_J_5_NO) ||
252 (xtse[6] & XTSE_7_02_J_5_O))
253 return ANNEX_J;
254
255 if ((xtse[4] & XTSE_5_03_L_3_NO) ||
256 (xtse[4] & XTSE_5_04_L_3_NO) ||
257 (xtse[4] & XTSE_5_05_L_3_O) ||
258 (xtse[4] & XTSE_5_06_L_3_O))
259 return ANNEX_L;
260
261 if ((xtse[4] & XTSE_5_07_M_3_NO) ||
262 (xtse[4] & XTSE_5_08_M_3_O) ||
263 (xtse[6] & XTSE_7_03_M_5_NO) ||
264 (xtse[6] & XTSE_7_04_M_5_O))
265 return ANNEX_M;
266
267 return ANNEX_UNKNOWN;
268 }
269
270 static standard_t get_standard(const uint8_t *xtse) {
271 if (xtse[0] & XTSE_1_01_A_T1_413)
272 return STD_T1_413;
273
274 if (xtse[0] & XTSE_1_02_C_TS_101388)
275 return STD_TS_101_388;
276
277 if ((xtse[0] & XTSE_1_03_A_1_NO) ||
278 (xtse[0] & XTSE_1_04_A_1_O) ||
279 (xtse[0] & XTSE_1_05_B_1_NO) ||
280 (xtse[0] & XTSE_1_06_B_1_O) ||
281 (xtse[0] & XTSE_1_07_C_1_NO) ||
282 (xtse[0] & XTSE_1_08_C_1_O))
283 return STD_G_992_1;
284
285 if ((xtse[1] & XTSE_2_01_A_2_NO) ||
286 (xtse[1] & XTSE_2_02_B_2_O) ||
287 (xtse[1] & XTSE_2_03_C_2_NO) ||
288 (xtse[1] & XTSE_2_04_C_2_O))
289 return STD_G_992_2;
290
291 if ((xtse[2] & XTSE_3_03_A_3_NO) ||
292 (xtse[2] & XTSE_3_04_A_3_O) ||
293 (xtse[2] & XTSE_3_05_B_3_NO) ||
294 (xtse[2] & XTSE_3_06_B_3_O) ||
295 (xtse[3] & XTSE_4_05_I_3_NO) ||
296 (xtse[3] & XTSE_4_06_I_3_O) ||
297 (xtse[3] & XTSE_4_07_J_3_NO) ||
298 (xtse[3] & XTSE_4_08_J_3_O) ||
299 (xtse[4] & XTSE_5_03_L_3_NO) ||
300 (xtse[4] & XTSE_5_04_L_3_NO) ||
301 (xtse[4] & XTSE_5_05_L_3_O) ||
302 (xtse[4] & XTSE_5_06_L_3_O) ||
303 (xtse[4] & XTSE_5_07_M_3_NO) ||
304 (xtse[4] & XTSE_5_08_M_3_O))
305 return STD_G_992_3;
306
307 if ((xtse[3] & XTSE_4_01_A_4_NO) ||
308 (xtse[3] & XTSE_4_02_A_4_O) ||
309 (xtse[4] & XTSE_5_01_I_4_NO) ||
310 (xtse[4] & XTSE_5_02_I_4_O))
311 return STD_G_992_4;
312
313 if ((xtse[5] & XTSE_6_01_A_5_NO) ||
314 (xtse[5] & XTSE_6_02_A_5_O) ||
315 (xtse[5] & XTSE_6_03_B_5_NO) ||
316 (xtse[5] & XTSE_6_04_B_5_O) ||
317 (xtse[5] & XTSE_6_07_I_5_NO) ||
318 (xtse[5] & XTSE_6_08_I_5_O) ||
319 (xtse[6] & XTSE_7_01_J_5_NO) ||
320 (xtse[6] & XTSE_7_02_J_5_O) ||
321 (xtse[6] & XTSE_7_03_M_5_NO) ||
322 (xtse[6] & XTSE_7_04_M_5_O))
323 return STD_G_992_5;
324
325 if (xtse[7] & XTSE_8_08)
326 return STD_G_993_1;
327
328 if ((xtse[7] & XTSE_8_01_A) ||
329 (xtse[7] & XTSE_8_02_B) ||
330 (xtse[7] & XTSE_8_03_C))
331 return STD_G_993_2;
332
333 return STD_UNKNOWN;
334 }
335
336 static void version_information(int fd) {
337 IOCTL(DSL_VersionInformation_t, DSL_FIO_VERSION_INFORMATION_GET)
338
339 m_str("api_version", out.data.DSL_DriverVersionApi);
340 m_str("firmware_version", out.data.DSL_ChipSetFWVersion);
341 m_str("chipset", out.data.DSL_ChipSetType);
342 m_str("driver_version", out.data.DSL_DriverVersionMeiBsp);
343 }
344
345 static void line_state(int fd) {
346 IOCTL(DSL_LineState_t, DSL_FIO_LINE_STATE_GET)
347
348 int map = LSTATE_MAP_UNKNOWN;
349 const char *str;
350 switch (out.data.nLineState) {
351 STR_CASE_MAP(DSL_LINESTATE_NOT_INITIALIZED, "Not initialized", LSTATE_MAP_NOT_INITIALIZED)
352 STR_CASE_MAP(DSL_LINESTATE_EXCEPTION, "Exception", LSTATE_MAP_EXCEPTION)
353 STR_CASE(DSL_LINESTATE_NOT_UPDATED, "Not updated")
354 STR_CASE(DSL_LINESTATE_IDLE_REQUEST, "Idle request")
355 STR_CASE_MAP(DSL_LINESTATE_IDLE, "Idle", LSTATE_MAP_IDLE)
356 STR_CASE(DSL_LINESTATE_SILENT_REQUEST, "Silent request")
357 STR_CASE_MAP(DSL_LINESTATE_SILENT, "Silent", LSTATE_MAP_SILENT)
358 STR_CASE_MAP(DSL_LINESTATE_HANDSHAKE, "Handshake", LSTATE_MAP_HANDSHAKE)
359 STR_CASE(DSL_LINESTATE_BONDING_CLR, "Bonding CLR")
360 STR_CASE_MAP(DSL_LINESTATE_FULL_INIT, "Full init", LSTATE_MAP_FULL_INIT)
361 STR_CASE(DSL_LINESTATE_SHORT_INIT_ENTRY, "Short init entry")
362 STR_CASE(DSL_LINESTATE_DISCOVERY, "Discovery")
363 STR_CASE(DSL_LINESTATE_TRAINING, "Training")
364 STR_CASE(DSL_LINESTATE_ANALYSIS, "Analysis")
365 STR_CASE(DSL_LINESTATE_EXCHANGE, "Exchange")
366 STR_CASE_MAP(DSL_LINESTATE_SHOWTIME_NO_SYNC, "Showtime without TC-Layer sync", LSTATE_MAP_SHOWTIME_NO_SYNC)
367 STR_CASE_MAP(DSL_LINESTATE_SHOWTIME_TC_SYNC, "Showtime with TC-Layer sync", LSTATE_MAP_SHOWTIME_TC_SYNC)
368 STR_CASE(DSL_LINESTATE_FASTRETRAIN, "Fastretrain")
369 STR_CASE(DSL_LINESTATE_LOWPOWER_L2, "Lowpower L2")
370 STR_CASE(DSL_LINESTATE_LOOPDIAGNOSTIC_ACTIVE, "Loopdiagnostic active")
371 STR_CASE(DSL_LINESTATE_LOOPDIAGNOSTIC_DATA_EXCHANGE, "Loopdiagnostic data exchange")
372 STR_CASE(DSL_LINESTATE_LOOPDIAGNOSTIC_DATA_REQUEST, "Loopdiagnostic data request")
373 STR_CASE(DSL_LINESTATE_LOOPDIAGNOSTIC_COMPLETE, "Loopdiagnostic complete")
374 STR_CASE_MAP(DSL_LINESTATE_RESYNC, "Resync", LSTATE_MAP_RESYNC)
375 STR_CASE(DSL_LINESTATE_TEST, "Test")
376 STR_CASE(DSL_LINESTATE_TEST_LOOP, "Test loop")
377 STR_CASE(DSL_LINESTATE_TEST_REVERB, "Test reverb")
378 STR_CASE(DSL_LINESTATE_TEST_MEDLEY, "Test medley")
379 STR_CASE(DSL_LINESTATE_TEST_SHOWTIME_LOCK, "Showtime lock")
380 STR_CASE(DSL_LINESTATE_TEST_QUIET, "Quiet")
381 STR_CASE(DSL_LINESTATE_LOWPOWER_L3, "Lowpower L3")
382 #ifndef INCLUDE_DSL_CPE_API_DANUBE
383 STR_CASE(DSL_LINESTATE_DISABLED, "Disabled")
384 STR_CASE(DSL_LINESTATE_T1413, "T1413")
385 STR_CASE(DSL_LINESTATE_ORDERLY_SHUTDOWN_REQUEST, "Orderly shutdown request")
386 STR_CASE(DSL_LINESTATE_ORDERLY_SHUTDOWN, "Orderly shutdown")
387 STR_CASE(DSL_LINESTATE_TEST_FILTERDETECTION_ACTIVE, "Test filterdetection active")
388 STR_CASE(DSL_LINESTATE_TEST_FILTERDETECTION_COMPLETE, "Test filterdetection complete")
389 #endif
390 default:
391 str = NULL;
392 break;
393 };
394
395 if (str)
396 m_str("state", str);
397
398 if (map != LSTATE_MAP_UNKNOWN )
399 m_u32("state_num", map);
400
401 m_bool("up", out.data.nLineState == DSL_LINESTATE_SHOWTIME_TC_SYNC);
402 }
403
404 static void pm_channel_counters_showtime(int fd) {
405 IOCTL_DIR(DSL_PM_ChannelCounters_t, DSL_FIO_PM_CHANNEL_COUNTERS_SHOWTIME_GET, DSL_NEAR_END);
406
407 m_u32("uptime", out.interval.nElapsedTime);
408 }
409
410 static void g997_line_inventory(int fd) {
411 IOCTL_DIR(DSL_G997_LineInventory_t, DSL_FIO_G997_LINE_INVENTORY_GET, DSL_DOWNSTREAM)
412
413 m_array("vendor_id", out.data.G994VendorID, DSL_G997_LI_MAXLEN_VENDOR_ID);
414 m_vendor("vendor", out.data.G994VendorID);
415 m_array("system_vendor_id", out.data.SystemVendorID, DSL_G997_LI_MAXLEN_VENDOR_ID);
416 m_vendor("system_vendor", out.data.SystemVendorID);
417 m_array("version", out.data.VersionNumber, DSL_G997_LI_MAXLEN_VERSION);
418 m_array("serial", out.data.SerialNumber, DSL_G997_LI_MAXLEN_SERIAL);
419 }
420
421 static void g977_get_bit_allocation(int fd, DSL_AccessDir_t direction) {
422 IOCTL_DIR(DSL_G997_BitAllocationNsc_t, DSL_FIO_G997_BIT_ALLOCATION_NSC_GET, direction);
423
424 // create default value to obtain consistent JSON structure
425 m_u32("groupsize", 1);
426 m_u32("groups", out.data.bitAllocationNsc.nNumData);
427 m_array("data", out.data.bitAllocationNsc.nNSCData, out.data.bitAllocationNsc.nNumData);
428 }
429
430 static void g977_get_snr(int fd, DSL_AccessDir_t direction) {
431 IOCTL_DIR_DELT(DSL_G997_DeltSnr_t, DSL_FIO_G997_DELT_SNR_GET, direction, DSL_DELT_DATA_SHOWTIME);
432
433 m_u32("groupsize", out.data.nGroupSize);
434 m_u32("groups", out.data.deltSnr.nNumData);
435
436 void *c = blobmsg_open_array(&b, "data");
437
438 // SNR -32 ... 95 dB
439 for (uint16_t i = 0 ; i < out.data.deltSnr.nNumData ; i++)
440 if (out.data.deltSnr.nNSCData[i] != 255 && out.data.deltSnr.nNSCData[i] != 0)
441 m_double("", -32 + (double)out.data.deltSnr.nNSCData[i] / 2);
442 else
443 m_null();
444
445 blobmsg_close_array(&b, c);
446 }
447
448 static void g977_get_qln(int fd, DSL_AccessDir_t direction) {
449 IOCTL_DIR_DELT(DSL_G997_DeltQln_t, DSL_FIO_G997_DELT_QLN_GET, direction, DSL_DELT_DATA_SHOWTIME);
450
451 m_u32("groupsize", out.data.nGroupSize);
452 m_u32("groups", out.data.deltQln.nNumData);
453
454 void *c = blobmsg_open_array(&b, "data");
455
456 // QLN -150 ... -23 dBm/Hz
457 for (uint16_t i = 0 ; i < out.data.deltQln.nNumData ; i++)
458 if (out.data.deltQln.nNSCData[i] != 255 && out.data.deltQln.nNSCData[i] != 0)
459 m_double("", -23 - (double)out.data.deltQln.nNSCData[i] / 2);
460 else
461 m_null();
462
463 blobmsg_close_array(&b, c);
464 }
465
466 static void g977_get_hlog(int fd, DSL_AccessDir_t direction) {
467 IOCTL_DIR_DELT(DSL_G997_DeltHlog_t, DSL_FIO_G997_DELT_HLOG_GET, direction, DSL_DELT_DATA_SHOWTIME);
468
469 m_u32("groupsize", out.data.nGroupSize);
470 m_u32("groups", out.data.deltHlog.nNumData);
471
472 void *c = blobmsg_open_array(&b, "data");
473
474 // HLOG +6 ... -96 dB
475 for (uint16_t i = 0 ; i < out.data.deltHlog.nNumData ; i++)
476 if (out.data.deltHlog.nNSCData[i] != 1023 && out.data.deltHlog.nNSCData[i] != 0)
477 m_double("", 6 - (double)out.data.deltHlog.nNSCData[i] / 10);
478 else
479 m_null();
480
481 blobmsg_close_array(&b, c);
482 }
483
484 static void g997_power_management_status(int fd) {
485 IOCTL(DSL_G997_PowerManagementStatus_t, DSL_FIO_G997_POWER_MANAGEMENT_STATUS_GET)
486
487 int map = PSTATE_MAP_UNKNOWN;
488 const char *str;
489 switch (out.data.nPowerManagementStatus) {
490 STR_CASE_MAP(DSL_G997_PMS_NA, "Power management state is not available", PSTATE_MAP_NA)
491 STR_CASE_MAP(DSL_G997_PMS_L0, "L0 - Synchronized", PSTATE_MAP_L0)
492 STR_CASE_MAP(DSL_G997_PMS_L1, "L1 - Power Down Data transmission (G.992.2)", PSTATE_MAP_L1)
493 STR_CASE_MAP(DSL_G997_PMS_L2, "L2 - Power Down Data transmission (G.992.3 and G.992.4)", PSTATE_MAP_L2)
494 STR_CASE_MAP(DSL_G997_PMS_L3, "L3 - No power", PSTATE_MAP_L3)
495 default:
496 str = NULL;
497 break;
498 };
499
500 if (str)
501 m_str("power_state", str);
502
503 if (map != PSTATE_MAP_UNKNOWN)
504 m_u32("power_state_num", map);
505 }
506
507 static void g997_xtu_system_enabling(int fd, standard_t *standard) {
508 IOCTL(DSL_G997_XTUSystemEnabling_t, DSL_FIO_G997_XTU_SYSTEM_ENABLING_STATUS_GET)
509
510 m_array("xtse", out.data.XTSE, DSL_G997_NUM_XTSE_OCTETS);
511
512 const char *str;
513 switch (get_annex(out.data.XTSE)) {
514 STR_CASE(ANNEX_A, "A")
515 STR_CASE(ANNEX_B, "B")
516 STR_CASE(ANNEX_C, "C")
517 STR_CASE(ANNEX_I, "I")
518 STR_CASE(ANNEX_J, "J")
519 STR_CASE(ANNEX_L, "L")
520 STR_CASE(ANNEX_M, "M")
521 default:
522 str = NULL;
523 break;
524 };
525 if (str)
526 m_str("annex", str);
527
528 *standard = get_standard(out.data.XTSE);
529
530 switch (*standard) {
531 STR_CASE(STD_T1_413, "T1.413")
532 STR_CASE(STD_TS_101_388, "TS 101 388")
533 STR_CASE(STD_G_992_1, "G.992.1")
534 STR_CASE(STD_G_992_2, "G.992.2")
535 STR_CASE(STD_G_992_3, "G.992.3")
536 STR_CASE(STD_G_992_4, "G.992.4")
537 STR_CASE(STD_G_992_5, "G.992.5")
538 STR_CASE(STD_G_993_1, "G.993.1")
539 STR_CASE(STD_G_993_2, "G.993.2")
540 default:
541 str = NULL;
542 break;
543 }
544 if (str)
545 m_str("standard", str);
546 }
547
548 static void get_vector_status(int fd, vector_t *status) {
549 *status = VECTOR_UNKNOWN;
550
551 #ifdef INCLUDE_DSL_CPE_API_VRX
552 if (fd < 0)
553 return;
554
555 IOCTL(IOCTL_MEI_dsmStatus_t, FIO_MEI_DSM_STATUS_GET);
556
557 switch (out.eVectorStatus) {
558 case e_MEI_VECTOR_STAT_OFF:
559 *status = VECTOR_OFF;
560 break;
561 case e_MEI_VECTOR_STAT_ON_DS:
562 *status = VECTOR_ON_DS;
563 break;
564 case e_MEI_VECTOR_STAT_ON_DS_US:
565 *status = VECTOR_ON_DS_US;
566 break;
567 default:
568 break;
569 };
570 #endif
571 }
572
573 static void vector_erb(int fd) {
574 #ifdef INCLUDE_DSL_CPE_API_VRX
575 if (fd < 0)
576 return;
577
578 IOCTL(IOCTL_MEI_dsmStatistics_t, FIO_MEI_DSM_STATISTICS_GET);
579
580 m_u32("sent", out.n_processed);
581 m_u32("discarded", out.n_fw_dropped_size + out.n_mei_dropped_size + out.n_mei_dropped_no_pp_cb + out.n_pp_dropped);
582 #endif
583 }
584
585 static void band_plan_status(int fd, profile_t *profile) {
586 #if (INCLUDE_DSL_CPE_API_VDSL_SUPPORT == 1)
587 IOCTL(DSL_BandPlanStatus_t, DSL_FIO_BAND_PLAN_STATUS_GET)
588
589 switch (out.data.nProfile) {
590 case DSL_PROFILE_8A:
591 *profile = PROFILE_8A;
592 break;
593 case DSL_PROFILE_8B:
594 *profile = PROFILE_8B;
595 break;
596 case DSL_PROFILE_8C:
597 *profile = PROFILE_8C;
598 break;
599 case DSL_PROFILE_8D:
600 *profile = PROFILE_8D;
601 break;
602 case DSL_PROFILE_12A:
603 *profile = PROFILE_12A;
604 break;
605 case DSL_PROFILE_12B:
606 *profile = PROFILE_12B;
607 break;
608 case DSL_PROFILE_17A:
609 *profile = PROFILE_17A;
610 break;
611 case DSL_PROFILE_30A:
612 *profile = PROFILE_30A;
613 break;
614 case DSL_PROFILE_35B:
615 *profile = PROFILE_35B;
616 break;
617 default:
618 *profile = PROFILE_UNKNOWN;
619 break;
620 };
621
622 const char *str;
623 switch (*profile) {
624 STR_CASE(PROFILE_8A, "8a")
625 STR_CASE(PROFILE_8B, "8b")
626 STR_CASE(PROFILE_8C, "8c")
627 STR_CASE(PROFILE_8D, "8d")
628 STR_CASE(PROFILE_12A, "12a")
629 STR_CASE(PROFILE_12B, "12b")
630 STR_CASE(PROFILE_17A, "17a")
631 STR_CASE(PROFILE_30A, "30a")
632 STR_CASE(PROFILE_35B, "35b")
633 default:
634 str = NULL;
635 break;
636 };
637 if (str)
638 m_str("profile", str);
639 #endif
640 }
641
642 static void line_feature_config(int fd, DSL_AccessDir_t direction) {
643 IOCTL_DIR(DSL_LineFeature_t, DSL_FIO_LINE_FEATURE_STATUS_GET, direction)
644
645 m_bool("trellis", out.data.bTrellisEnable);
646 m_bool("bitswap", out.data.bBitswapEnable);
647 m_bool("retx", out.data.bReTxEnable);
648 m_bool("virtual_noise", out.data.bVirtualNoiseSupport);
649 }
650
651 static void g997_channel_status(int fd, DSL_AccessDir_t direction) {
652 IOCTL_DIR(DSL_G997_ChannelStatus_t, DSL_FIO_G997_CHANNEL_STATUS_GET, direction);
653
654 m_u32("interleave_delay", out.data.ActualInterleaveDelay * 10);
655 #ifndef INCLUDE_DSL_CPE_API_DANUBE
656 // prefer ACTNDR, see comments in drv_dsl_cpe_api_g997.h
657 m_u32("data_rate", out.data.ActualNetDataRate);
658 #else
659 m_u32("data_rate", out.data.ActualDataRate);
660 #endif
661 }
662
663 static void g997_line_status(int fd, DSL_AccessDir_t direction) {
664 IOCTL_DIR_DELT(DSL_G997_LineStatus_t, DSL_FIO_G997_LINE_STATUS_GET, direction, DSL_DELT_DATA_SHOWTIME);
665
666 // invalid value indicators taken from drv_dsl_cpe_api_g997.h
667 m_db("latn", out.data.LATN, 1271);
668 m_db("satn", out.data.SATN, 1271);
669 m_db("snr", out.data.SNR, -641);
670 m_db("actps", out.data.ACTPS, -901);
671 m_db("actatp", out.data.ACTATP, -512);
672 m_u32("attndr", out.data.ATTNDR);
673 }
674
675 static void pm_line_sec_counters_total(int fd, DSL_XTUDir_t direction) {
676 IOCTL_DIR(DSL_PM_LineSecCountersTotal_t, DSL_FIO_PM_LINE_SEC_COUNTERS_TOTAL_GET, direction)
677
678 m_u32("es", out.data.nES);
679 m_u32("ses", out.data.nSES);
680 m_u32("loss", out.data.nLOSS);
681 m_u32("uas", out.data.nUAS);
682 m_u32("lofs", out.data.nLOFS);
683 #ifndef INCLUDE_DSL_CPE_API_DANUBE
684 m_u32("fecs", out.data.nFECS);
685 #endif
686 }
687
688 static void pm_data_path_counters_total(int fd, DSL_XTUDir_t direction) {
689 IOCTL_DIR(DSL_PM_DataPathCountersTotal_t, DSL_FIO_PM_DATA_PATH_COUNTERS_TOTAL_GET, direction);
690
691 m_u32("hec", out.data.nHEC);
692 m_u32("ibe", out.data.nIBE);
693 m_u32("crc_p", out.data.nCRC_P);
694 m_u32("crcp_p", out.data.nCRCP_P);
695 m_u32("cv_p", out.data.nCV_P);
696 m_u32("cvp_p", out.data.nCVP_P);
697 }
698
699 static void retx_statistics(int fd, DSL_XTUDir_t direction) {
700 #ifdef INCLUDE_DSL_CPE_PM_RETX_COUNTERS
701 #ifdef INCLUDE_DSL_CPE_PM_RETX_THRESHOLDS
702 IOCTL_DIR(DSL_ReTxStatistics_t, DSL_FIO_RETX_STATISTICS_GET, direction);
703
704 m_u32("rx_corrupted", out.data.nRxCorruptedTotal);
705 m_u32("rx_uncorrected_protected", out.data.nRxUncorrectedProtected);
706 m_u32("rx_retransmitted", out.data.nRxRetransmitted);
707 m_u32("rx_corrected", out.data.nRxCorrected);
708 m_u32("tx_retransmitted", out.data.nTxRetransmitted);
709 #endif
710 #endif
711 }
712
713 static void describe_mode(standard_t standard, profile_t profile, vector_t vector) {
714 char buf[128];
715
716 switch (standard) {
717 case STD_T1_413:
718 strcpy(buf, "T1.413");
719 break;
720 case STD_TS_101_388:
721 strcpy(buf, "TS 101 388");
722 break;
723 case STD_G_992_1:
724 strcpy(buf, "G.992.1 (ADSL)");
725 break;
726 case STD_G_992_2:
727 strcpy(buf, "G.992.2 (ADSL lite)");
728 break;
729 case STD_G_992_3:
730 strcpy(buf, "G.992.3 (ADSL2)");
731 break;
732 case STD_G_992_4:
733 strcpy(buf, "G.992.4 (ADSL2 lite)");
734 break;
735 case STD_G_992_5:
736 strcpy(buf, "G.992.5 (ADSL2+)");
737 break;
738 case STD_G_993_1:
739 strcpy(buf, "G.993.1 (VDSL)");
740 break;
741 case STD_G_993_2:
742 strcpy(buf, "G.993.2 (VDSL2");
743
744 switch (profile) {
745 case PROFILE_8A:
746 strcat(buf, ", Profile 8a");
747 break;
748 case PROFILE_8B:
749 strcat(buf, ", Profile 8b");
750 break;
751 case PROFILE_8C:
752 strcat(buf, ", Profile 8c");
753 break;
754 case PROFILE_8D:
755 strcat(buf, ", Profile 8d");
756 break;
757 case PROFILE_12A:
758 strcat(buf, ", Profile 12a");
759 break;
760 case PROFILE_12B:
761 strcat(buf, ", Profile 12b");
762 break;
763 case PROFILE_17A:
764 strcat(buf, ", Profile 17a");
765 break;
766 case PROFILE_30A:
767 strcat(buf, ", Profile 30a");
768 break;
769 case PROFILE_35B:
770 strcat(buf, ", Profile 35b");
771 break;
772 default:
773 break;
774 };
775
776 switch (vector) {
777 case VECTOR_ON_DS:
778 strcat(buf, ", with downstream vectoring");
779 break;
780 case VECTOR_ON_DS_US:
781 strcat(buf, ", with down- and upstream vectoring");
782 break;
783 default:
784 break;
785 };
786
787 strcat(buf, ")");
788 break;
789 default:
790 return;
791 };
792
793 m_str("mode", buf);
794 }
795
796 static int line_statistics(struct ubus_context *ctx, struct ubus_object *obj,
797 struct ubus_request_data *req, const char *method,
798 struct blob_attr *msg)
799 {
800 int fd;
801 void *c, *c2;
802
803 #ifndef INCLUDE_DSL_CPE_API_DANUBE
804 fd = open(DSL_CPE_DEVICE_NAME "/0", O_RDWR, 0644);
805 #else
806 fd = open(DSL_CPE_DEVICE_NAME, O_RDWR, 0644);
807 #endif
808 if (fd < 0)
809 return UBUS_STATUS_UNKNOWN_ERROR;
810
811 blob_buf_init(&b, 0);
812
813 c = blobmsg_open_table(&b, "bits");
814 c2 = blobmsg_open_table(&b, "downstream");
815 g977_get_bit_allocation(fd, DSL_DOWNSTREAM);
816 blobmsg_close_table(&b, c2);
817 c2 = blobmsg_open_table(&b, "upstream");
818 g977_get_bit_allocation(fd, DSL_UPSTREAM);
819 blobmsg_close_table(&b, c2);
820 blobmsg_close_table(&b, c);
821
822 c = blobmsg_open_table(&b, "snr");
823 c2 = blobmsg_open_table(&b, "downstream");
824 g977_get_snr(fd, DSL_DOWNSTREAM);
825 blobmsg_close_table(&b, c2);
826 c2 = blobmsg_open_table(&b, "upstream");
827 g977_get_snr(fd, DSL_UPSTREAM);
828 blobmsg_close_table(&b, c2);
829 blobmsg_close_table(&b, c);
830
831 c = blobmsg_open_table(&b, "qln");
832 c2 = blobmsg_open_table(&b, "downstream");
833 g977_get_qln(fd, DSL_DOWNSTREAM);
834 blobmsg_close_table(&b, c2);
835 c2 = blobmsg_open_table(&b, "upstream");
836 g977_get_qln(fd, DSL_UPSTREAM);
837 blobmsg_close_table(&b, c2);
838 blobmsg_close_table(&b, c);
839
840 c = blobmsg_open_table(&b, "hlog");
841 c2 = blobmsg_open_table(&b, "downstream");
842 g977_get_hlog(fd, DSL_DOWNSTREAM);
843 blobmsg_close_table(&b, c2);
844 c2 = blobmsg_open_table(&b, "upstream");
845 g977_get_hlog(fd, DSL_UPSTREAM);
846 blobmsg_close_table(&b, c2);
847 blobmsg_close_table(&b, c);
848
849 ubus_send_reply(ctx, req, b.head);
850
851 close(fd);
852
853 return 0;
854 }
855
856 static int metrics(struct ubus_context *ctx, struct ubus_object *obj,
857 struct ubus_request_data *req, const char *method,
858 struct blob_attr *msg)
859 {
860 int fd, fd_mei;
861 void *c, *c2;
862 standard_t standard = STD_UNKNOWN;
863 profile_t profile = PROFILE_UNKNOWN;
864 vector_t vector = VECTOR_UNKNOWN;
865
866 #ifndef INCLUDE_DSL_CPE_API_DANUBE
867 fd = open(DSL_CPE_DEVICE_NAME "/0", O_RDWR, 0644);
868 #else
869 fd = open(DSL_CPE_DEVICE_NAME, O_RDWR, 0644);
870 #endif
871 if (fd < 0)
872 return UBUS_STATUS_UNKNOWN_ERROR;
873
874 #ifdef INCLUDE_DSL_CPE_API_VRX
875 fd_mei = open(DSL_CPE_DSL_LOW_DEV "/0", O_RDWR, 0644);
876 #else
877 fd_mei = -1;
878 #endif
879
880 blob_buf_init(&b, 0);
881
882 version_information(fd);
883 line_state(fd);
884 pm_channel_counters_showtime(fd);
885
886 c = blobmsg_open_table(&b, "atu_c");
887 g997_line_inventory(fd);
888 blobmsg_close_table(&b, c);
889
890 g997_power_management_status(fd);
891 g997_xtu_system_enabling(fd, &standard);
892
893 if (standard == STD_G_993_2) {
894 band_plan_status(fd, &profile);
895 get_vector_status(fd_mei, &vector);
896 }
897
898 describe_mode(standard, profile, vector);
899
900 c = blobmsg_open_table(&b, "upstream");
901 switch (vector) {
902 case VECTOR_OFF:
903 m_bool("vector", false);
904 break;
905 case VECTOR_ON_DS_US:
906 m_bool("vector", true);
907 break;
908 default:
909 break;
910 };
911 line_feature_config(fd, DSL_UPSTREAM);
912 g997_channel_status(fd, DSL_UPSTREAM);
913 g997_line_status(fd, DSL_UPSTREAM);
914 blobmsg_close_table(&b, c);
915
916 c = blobmsg_open_table(&b, "downstream");
917 switch (vector) {
918 case VECTOR_OFF:
919 m_bool("vector", false);
920 break;
921 case VECTOR_ON_DS:
922 case VECTOR_ON_DS_US:
923 m_bool("vector", true);
924 break;
925 default:
926 break;
927 };
928 line_feature_config(fd, DSL_DOWNSTREAM);
929 g997_channel_status(fd, DSL_DOWNSTREAM);
930 g997_line_status(fd, DSL_DOWNSTREAM);
931 blobmsg_close_table(&b, c);
932
933 c = blobmsg_open_table(&b, "errors");
934 c2 = blobmsg_open_table(&b, "near");
935 pm_line_sec_counters_total(fd, DSL_NEAR_END);
936 pm_data_path_counters_total(fd, DSL_NEAR_END);
937 retx_statistics(fd, DSL_NEAR_END);
938 blobmsg_close_table(&b, c2);
939
940 c2 = blobmsg_open_table(&b, "far");
941 pm_line_sec_counters_total(fd, DSL_FAR_END);
942 pm_data_path_counters_total(fd, DSL_FAR_END);
943 retx_statistics(fd, DSL_FAR_END);
944 blobmsg_close_table(&b, c2);
945 blobmsg_close_table(&b, c);
946
947 switch (vector) {
948 case VECTOR_ON_DS:
949 case VECTOR_ON_DS_US:
950 c = blobmsg_open_table(&b, "erb");
951 vector_erb(fd_mei);
952 blobmsg_close_table(&b, c);
953 break;
954 default:
955 break;
956 };
957
958 ubus_send_reply(ctx, req, b.head);
959
960 if (fd_mei >= 0)
961 close(fd_mei);
962 close(fd);
963
964 return 0;
965 }
966
967 static const struct ubus_method dsl_methods[] = {
968 UBUS_METHOD_NOARG("metrics", metrics),
969 UBUS_METHOD_NOARG("statistics", line_statistics)
970 };
971
972 static struct ubus_object_type dsl_object_type =
973 UBUS_OBJECT_TYPE("dsl", dsl_methods);
974
975 static struct ubus_object dsl_object = {
976 .name = "dsl",
977 .type = &dsl_object_type,
978 .methods = dsl_methods,
979 .n_methods = ARRAY_SIZE(dsl_methods),
980 };
981
982 static DSL_int_t ubus_main(DSL_CPE_Thread_Params_t *params) {
983 uloop_run();
984 return 0;
985 }
986
987 void ubus_init() {
988 uloop_init();
989
990 ctx = ubus_connect(NULL);
991 if (!ctx)
992 return;
993
994 if (ubus_add_object(ctx, &dsl_object)) {
995 ubus_free(ctx);
996 ctx = NULL;
997 return;
998 }
999
1000 ubus_add_uloop(ctx);
1001
1002 DSL_CPE_ThreadInit(&thread, "ubus", ubus_main, DSL_CPE_PIPE_STACK_SIZE, DSL_CPE_PIPE_PRIORITY, 0, 0);
1003 }
1004
1005 void ubus_deinit() {
1006 if (!ctx)
1007 return;
1008
1009 ubus_free(ctx);
1010 uloop_done();
1011
1012 DSL_CPE_ThreadShutdown(&thread, 1000);
1013 }