From 8f926691eeab41ade8c419a7c5706d0e91b5f87d Mon Sep 17 00:00:00 2001
From: Yalu Zhang <yalu.zhang@iopsys.eu>
Date: Wed, 18 Mar 2020 10:38:00 +0100
Subject: [PATCH] Merge from branch documentation and suppress multiple
 commitment into one

Add functional API test JSON files for the following objects
    - FAST
    - ATM
    - PTM
Also update DSL by adding configure mthodAdd functional API test JSON files for the following objects

Add JSON schemas to the output of the following ubus calls
    - fast.line status and stats
    - atm.link status and stats
    - ptm.line status

Add and update documents for newly added functions
    - DSL configure
    - ATM
    - PTM
    - FAST

Add unit-test cases for the configuration if DSL and ATM

Both positive and negative cases are included

Add UBUS objects for the configuration of DSL and ATM

Add UBUS objects for the status and statistics of ATM and PTM links

Add UBUS objects fast.line.x
---
 README.md                                  |  28 +-
 docs/api/atm.link.md                       | 157 ++++
 docs/api/dsl.line.md                       |  48 ++
 docs/api/fast.line.md                      | 264 +++++++
 docs/api/ptm.link.md                       |  43 ++
 docs/testspec.md                           | 104 ++-
 docs/ubus.splash.md                        |   3 +
 dslmngr.c                                  | 842 ++++++++++++++++++++-
 dslmngr.h                                  |  23 +-
 schemas/ubus/atm.link_stats.json           |  32 +
 schemas/ubus/atm.link_status.json          |  82 ++
 schemas/ubus/fast.line_stats.json          |  82 ++
 schemas/ubus/fast.line_stats_interval.json |  50 ++
 schemas/ubus/fast.line_status.json         | 122 +++
 schemas/ubus/ptm.link_status.json          |  37 +
 test/api/json/atm.link.validation.json     |  30 +
 test/api/json/dsl.line.validation.json     |  13 +
 test/api/json/fast.line.validation.json    |  55 ++
 test/api/json/ptm.link.validation.json     |  10 +
 test/cmocka/unit_test_dslmngr.c            | 223 +++++-
 20 files changed, 2210 insertions(+), 38 deletions(-)
 create mode 100644 docs/api/atm.link.md
 create mode 100644 docs/api/fast.line.md
 create mode 100644 docs/api/ptm.link.md
 create mode 100644 schemas/ubus/atm.link_stats.json
 create mode 100644 schemas/ubus/atm.link_status.json
 create mode 100644 schemas/ubus/fast.line_stats.json
 create mode 100644 schemas/ubus/fast.line_stats_interval.json
 create mode 100644 schemas/ubus/fast.line_status.json
 create mode 100644 schemas/ubus/ptm.link_status.json
 create mode 100644 test/api/json/atm.link.validation.json
 create mode 100644 test/api/json/fast.line.validation.json
 create mode 100644 test/api/json/ptm.link.validation.json

diff --git a/README.md b/README.md
index 8ff44e3..a48901e 100644
--- a/README.md
+++ b/README.md
@@ -65,15 +65,25 @@ config ptm-device 'ptm0'
 This is a verbose print of all methods published to ubus on a device.
 
 ```bash
-'dsl' @d35e2f41
-	"status":{}
-	"stats":{}
-'dsl.channel.0' @9f3ca1b2
-	"status":{}
-	"stats":{"interval":"String"}
-'dsl.line.0' @c01eec72
-	"status":{}
-	"stats":{"interval":"String"}
+'atm.link.0' @607d41d6
+        "status":{}
+        "stats":{}
+        "configure":{"link_type":"String","vpi":"Integer","vci":"Integer","encapsulation":"String","qos_class":"String","peak_cell_rate":"Integer","max_burst_size":"Integer","sustainable_cell_rate":"Integer"}
+'dsl' @8e73ca86
+        "status":{}
+        "stats":{}
+'dsl.channel.0' @e76e0883
+        "status":{}
+        "stats":{"interval":"String"}
+'dsl.line.0' @3a06e2d2
+        "status":{}
+        "stats":{"interval":"String"}
+        "configure":{"xtse":"String","vdsl2_profiles":"String","fast_profiles":"String","data_gathering":"Boolean","limit_mask":"Integer","us0_mask":"Integer"}
+'fast.line.0' @bc51c0a7
+        "status":{}
+        "stats":{"interval":"String"}
+'ptm.link.0' @35822197
+        "status":{}
 ```
 
 For more info on the Ubus API see [link](./docs/ubus.splash.md)
diff --git a/docs/api/atm.link.md b/docs/api/atm.link.md
new file mode 100644
index 0000000..1266d32
--- /dev/null
+++ b/docs/api/atm.link.md
@@ -0,0 +1,157 @@
+# atm.link.&lt;number&gt;
+
+| List of Methods                   |
+| --- |
+| [status](#status)                 |
+| [stats](#stats)                   |
+| [configure](#configure)           |
+
+## status
+
+`status`
+
+Type: `Method`
+
+### Ubus CLI Example
+
+```bash
+ubus call atm.link.0 status
+```
+
+### JSONRPC Example
+
+```json
+{
+  "jsonrpc": "2.0",
+  "id": 0,
+  "method": "call",
+  "params": ["<SID>", "atm.link.0", "status"]
+}
+```
+
+### Input
+
+None
+
+### Output
+
+- type: `object`
+- Example as below
+```json
+{
+        "status": "up",
+        "link_type": "eoa",
+        "auto_config": true,
+        "dest_addr": {
+                "vpi": 8,
+                "vci": 35
+        },
+        "encapsulation": "llc",
+        "fcs_preserved": true,
+        "vc_search_list": [
+                {
+                        "vpi": 8,
+                        "vci": 36
+                },
+                {
+                        "vpi": 8,
+                        "vci": 37
+                }
+        ],
+        "aal": "aal5",
+        "qos": {
+                "qos_class": "ubr",
+                "peak_cell_rate": 18867,
+                "max_burst_size": 22640,
+                "sustainable_cell_rate": 15093
+        }
+}
+```
+
+## stats
+
+`stats`
+
+Type: `Method`
+
+### Ubus CLI Example
+
+```bash
+ubus call atm.link.0 stats
+```
+
+### JSONRPC Example
+
+```json
+{
+  "jsonrpc": "2.0",
+  "id": 0,
+  "method": "call",
+  "params": ["<SID>", "atm.link.0", "stats"]
+}
+```
+
+### Input
+
+None
+
+### Output
+
+- type: `object`
+- Example as below
+```json
+{
+        "transmitted_blocks": 12345678,
+        "received_blocks": 87654321,
+        "crc_errors": 12
+}
+```
+
+## configure
+
+`configure {...}`
+
+Type: `Method`
+
+### Ubus CLI Example
+
+```bash
+ubus call atm.link.0 configure "{'link_type':'eoa','vpi':8,'vci':35,'encapsulation':'llc',\
+    'qos_class':'ubr','peak_cell_rate':10240,'max_burst_size':8124,'sustainable_cell_rate':9600}"
+```
+
+### JSONRPC Example
+
+```json
+{
+  "jsonrpc": "2.0",
+  "id": 0,
+  "method": "call",
+  "params": ["<SID>", "atm.link.0", "configure", "{'link_type':'eoa','vpi':8,'vci':35,'encapsulation':'llc'}"]
+}
+```
+
+### Input
+
+- Type: `object`
+- Parameter: `link_type`
+- Value: ATM link type with one of values "eoa", "ipoa", "pppoa" and "cip"
+- Parameer:`vpi`
+- Value: unsigned int as virtual path index
+- Parameer:`vci`
+- Value: unsigned int as virtual circuit index
+- Parameer:`encapsulation`
+- Value: ATM link encapsulation with one of values "llc" and "vcmux"
+- Parameer:`qos_class`
+- Value: ATM QoS class with one of values "ubr", "cbr", "gfr", "vbr_nrt", "vbr_rt", "ubr_plus" and "abr"
+- Parameer:`peak_cell_rate`
+- Value: unsigned integer
+- Parameer:`max_burst_size`
+- Value: unsigned integer
+- Parameer:`sustainable_cell_rate`
+- Value: unsigned integer
+
+### Output
+
+None
+
diff --git a/docs/api/dsl.line.md b/docs/api/dsl.line.md
index a190411..8620141 100644
--- a/docs/api/dsl.line.md
+++ b/docs/api/dsl.line.md
@@ -5,6 +5,7 @@
 | [status](#status)                 |
 | [stats](#stats)                   |
 | [stats_interval](#stats_interval) |
+| [configure](#configure)           |
 
 ## status
 
@@ -247,3 +248,50 @@ ubus call dsl.line.0 stats "{ 'interval': 'total'}"
     "severely_errored_secs": 0
 }
 ```
+
+## configure
+
+`configure {...}`
+
+Type: `Method`
+
+### Ubus CLI Example
+
+```bash
+ubus call dsl.line.0 configure "{'xtse':'01,02,ab,cd,41,52,f8,6e',\
+    'vdsl2_profiles':'8a,8b,8c,8d,17a,30a,35b',\
+    'fast_profiles':'106a,212a','data_gathering':true,'limit_mask':255,'us0_mask':511}"
+```
+
+### JSONRPC Example
+
+```json
+{
+  "jsonrpc": "2.0",
+  "id": 0,
+  "method": "call",
+  "params": ["<SID>", "dsl.line.0", "configure", "{'xtse':'01,02,ab,cd,41,52,f8,6e','vdsl2_profiles':'8a,8b,8c,8d,17a,30a,35b'}"]
+}
+```
+
+### Input
+
+- Type: `object`
+- Parameter: `xtse`
+- Value: 8 octects which include all modes supported by the DSL line
+- Parameer:`vdsl2_profiles`
+- Value: Sub/full set of "8a,8b,8c,8d,17a,30a,35b" of VDSL2 profiles
+- Parameer:`fast_profiles`
+- Value: Sub/full set of "106a,212a" of FAST profiles
+- Parameer:`data_gathering`
+- Value: true or false to enale/disable data gathering
+- Parameer:`limit_mask`
+- Value: unsigned integer as limit mask
+- Parameer:`us0_mask`
+- Value: unsigned integer as us0 mask
+
+
+### Output
+
+None
+
diff --git a/docs/api/fast.line.md b/docs/api/fast.line.md
new file mode 100644
index 0000000..9a90ad9
--- /dev/null
+++ b/docs/api/fast.line.md
@@ -0,0 +1,264 @@
+# fast.line.&lt;number&gt;
+
+| List of Methods                   |
+| --- |
+| [status](#status)                 |
+| [stats](#stats)                   |
+| [stats_interval](#stats_interval) |
+
+## status
+
+`status`
+
+Type: `Method`
+
+### Ubus CLI Example
+
+```bash
+ubus call fast.line.0 status
+```
+
+### JSONRPC Example
+
+```json
+{
+  "jsonrpc": "2.0",
+  "id": 0,
+  "method": "call",
+  "params": ["<SID>", "fast.line.0", "status"]
+}
+```
+
+### Input
+
+None
+
+### Output
+
+- type: `object`
+- Example as below
+```json
+{
+        "status": "up",
+        "upstream": true,
+        "firmware_version": "A2pvfbH045k.d28a",
+        "link_status": "up",
+        "current_profile": "106a",
+        "power_management_state": "l0",
+        "max_bit_rate": {
+                "us": 201000,
+                "ds": 402000
+        },
+        "allowed_profiles": [
+                "106a",
+                "212a"
+        ],
+        "success_failure_cause": 0,
+        "upbokler": 180,
+        "last_transmitted_signal": {
+                "us": 100,
+                "ds": 200
+        },
+        "upbokle": 160,
+        "line_number": 1,
+        "noise_margin": {
+                "us": 146,
+                "ds": 177
+        },
+        "attenuation": {
+                "us": 0,
+                "ds": 48
+        },
+        "power": {
+                "us": 123,
+                "ds": 106
+        },
+        "snrm_rmc": {
+                "us": 12,
+                "ds": 14
+        },
+        "bits_rmc_ps": [
+                1,
+                3,
+                5,
+                7
+        ],
+        "fext_to_cancel_enable": {
+                "us": true,
+                "ds": true
+        },
+        "etr": {
+                "us": 250000,
+                "ds": 350000
+        },
+        "att_etr": {
+                "us": 225000,
+                "ds": 325000
+        },
+        "min_eftr": {
+                "us": 200000,
+                "ds": 300000
+        }
+}
+```
+
+## stats
+
+`stats`
+
+Type: `Method`
+
+### Ubus CLI Example
+
+```bash
+ubus call fast.line.0 stats
+```
+
+### JSONRPC Example
+
+```json
+{
+  "jsonrpc": "2.0",
+  "id": 0,
+  "method": "call",
+  "params": ["<SID>", "fast.line.0", "stats"]
+}
+```
+
+### Input
+
+None
+
+### Output
+
+- type: `object`
+- Example as below
+```json
+{
+        "total_start": 397,
+        "showtime_start": 349,
+        "last_showtime_start": 349,
+        "current_day_start": 397,
+        "quarter_hour_start": 397,
+        "total": {
+                "errored_secs": 20,
+                "severely_errored_secs": 5,
+                "loss": 1,
+                "lors": 4294967295,
+                "uas": 1,
+                "rtx_uc": 4294967295,
+                "rtx_tx": 0,
+                "success_bsw": 1024,
+                "success_sra": 2048,
+                "success_fra": 1024,
+                "success_rpa": 2048,
+                "success_tiga": 1024
+        },
+        "showtime": {
+                "errored_secs": 20,
+                "severely_errored_secs": 5,
+                "loss": 1,
+                "lors": 4294967295,
+                "uas": 1,
+                "rtx_uc": 4294967295,
+                "rtx_tx": 0,
+                "success_bsw": 1024,
+                "success_sra": 2048,
+                "success_fra": 1024,
+                "success_rpa": 2048,
+                "success_tiga": 1024
+        },
+        "lastshowtime": {
+                "errored_secs": 20,
+                "severely_errored_secs": 5,
+                "loss": 1,
+                "lors": 4294967295,
+                "uas": 1,
+                "rtx_uc": 4294967295,
+                "rtx_tx": 0,
+                "success_bsw": 1024,
+                "success_sra": 2048,
+                "success_fra": 1024,
+                "success_rpa": 2048,
+                "success_tiga": 1024
+        },
+        "currentday": {
+                "errored_secs": 20,
+                "severely_errored_secs": 5,
+                "loss": 1,
+                "lors": 4294967295,
+                "uas": 1,
+                "rtx_uc": 4294967295,
+                "rtx_tx": 0,
+                "success_bsw": 1024,
+                "success_sra": 2048,
+                "success_fra": 1024,
+                "success_rpa": 2048,
+                "success_tiga": 1024
+        },
+        "quarterhour": {
+                "errored_secs": 20,
+                "severely_errored_secs": 5,
+                "loss": 1,
+                "lors": 4294967295,
+                "uas": 1,
+                "rtx_uc": 4294967295,
+                "rtx_tx": 0,
+                "success_bsw": 1024,
+                "success_sra": 2048,
+                "success_fra": 1024,
+                "success_rpa": 2048,
+                "success_tiga": 1024
+        }
+}
+```
+
+## stats_interval
+
+`stats <interval>`
+
+Type: `Method`
+
+### Ubus CLI Example
+
+```bash
+ubus call fast.line.0 stats "{ 'interval': 'total'}"
+```
+
+### JSONRPC Example
+
+```json
+{
+  "jsonrpc": "2.0",
+  "id": 0,
+  "method": "call",
+  "params": ["<SID>", "fast.line.0", "stats", "{ 'interval': 'total'}"]
+}
+```
+
+### Input
+
+- Type: `object`
+- Parameter: `interval`
+- Value: `total`, `showtime`, `lastshowtime`, `currentday`, or `quarterhour`
+
+### Output
+
+- type: `object`
+- Example as below
+```json
+{
+        "errored_secs": 20,
+        "severely_errored_secs": 5,
+        "loss": 1,
+        "lors": 4294967295,
+        "uas": 1,
+        "rtx_uc": 4294967295,
+        "rtx_tx": 0,
+        "success_bsw": 1024,
+        "success_sra": 2048,
+        "success_fra": 1024,
+        "success_rpa": 2048,
+        "success_tiga": 1024
+}
+```
diff --git a/docs/api/ptm.link.md b/docs/api/ptm.link.md
new file mode 100644
index 0000000..5e8ecbf
--- /dev/null
+++ b/docs/api/ptm.link.md
@@ -0,0 +1,43 @@
+# ptm.link.&lt;number&gt;
+
+| List of Methods                   |
+| --- |
+| [status](#status)                 |
+
+## status
+
+`status`
+
+Type: `Method`
+
+### Ubus CLI Example
+
+```bash
+ubus call ptm.link.0 status
+```
+
+### JSONRPC Example
+
+```json
+{
+  "jsonrpc": "2.0",
+  "id": 0,
+  "method": "call",
+  "params": ["<SID>", "ptm.link.0", "status"]
+}
+```
+
+### Input
+
+None
+
+### Output
+
+- type: `object`
+- Example as below
+```json
+{
+        "status": "up",
+        "mac_addr": "00:11:22:33:44:55"
+}
+```
diff --git a/docs/testspec.md b/docs/testspec.md
index b937640..68844b8 100644
--- a/docs/testspec.md
+++ b/docs/testspec.md
@@ -29,6 +29,7 @@ using JSON-C library APIs.
 | 1				| status     	| No argument  	| [1](./api/dsl.line.md#status) |
 | 2				| stats     	| No argument	  | [2](./api/dsl.line.md#stats)  |
 | 3				| stats     	| Interval	    | [3](./api/dsl.line.md#stats_interval) |
+| 9                             | configure     | DSL_config          | [9](./api/dsl.line.md#configure) |
 
 #### dsl.channel.&lt;number&gt;
 
@@ -45,6 +46,28 @@ using JSON-C library APIs.
 | 7				| status     	| No argument  	| [7](./api/dsl.md#status) |
 | 8				| stats     	| No argument	  | [8](./api/dsl.md#stats)  |
 
+#### fast.line.&lt;number&gt;
+
+| Execution ID  | Method        | Description   | Function ID Coverage                  |
+| :---                  | :---                  | :---                  | :---                                                  |
+| 10                             | status        | No argument   | [10](./api/fast.line.md#status) |
+| 11                             | stats         | No argument     | [11](./api/fast.line.md#stats)  |
+| 12                             | stats         | Interval          | [12](./api/fast.line.md#stats_interval) |
+
+#### atm.link.&lt;number&gt;
+
+| Execution ID  | Method        | Description   | Function ID Coverage                  |
+| :---                  | :---                  | :---                  | :---                                                  |
+| 13                             | status        | No argument   | [13](./api/atm.link.md#status) |
+| 14                             | stats         | No argument     | [14](./api/atm.link.md#stats)  |
+| 15                            | configure     | ATM_config          | [15](./api/atm.link.md#configure) |
+
+#### ptm.link.&lt;number&gt;
+
+| Execution ID  | Method        | Description   | Function ID Coverage                  |
+| :---                  | :---                  | :---                  | :---                                                  |
+| 16                             | status        | No argument   | [16](./api/ptm.link.md#status) |
+
 ### Unit Tests
 
 The *dslmngr* unit tests are written in CMocka, invoking the UBUS callbacks directly from the source code, which is compiled
@@ -53,16 +76,20 @@ into a shared library. This means mocking the arguments of a CLI or libubus invo
 The output of the UBUS callbacks are retrieved by manipulation of the function ubus_send_reply(). Then the output is analyzed
 and verified by each test case.
 
-| Execution ID	| Method      	   | Test Case Name 	                   | Function ID Coverage			                     |
+| Execution ID	| Method      	   | Test Case Name 	                   | Function ID Coverage			                  |
 | :---    | :---                   | :---                                | :---                                          |
-| 1				| line_status     	     | test_api_dsl_line_status            | [1](./api/dsl.line.md#status)            |
-| 2				| line_stats     	       | test_api_dsl_line_stats             | [2](./api/dsl.line.md#stats)             |
+| 1				| line_status     	 | test_api_dsl_line_status            | [1](./api/dsl.line.md#status)            |
+| 2				| line_stats     	 | test_api_dsl_line_stats             | [2](./api/dsl.line.md#stats)             |
 | 3				| line_stats_interval  	 | test_api_dsl_line_stats_interval    | [3](./api/dsl.line.md#stats_interval)    |
 | 4				| channel_status         | test_api_dsl_channel_status         | [4](./api/dsl.channel.md#status)         |
-| 5				| channel_stats     	   | test_api_dsl_channel_stats          | [5](./api/dsl.channel.md#stats)          |
+| 5				| channel_stats     	 | test_api_dsl_channel_stats          | [5](./api/dsl.channel.md#stats)          |
 | 6				| channel_stats_interval | test_api_dsl_channel_stats_interval | [6](./api/dsl.channel.md#stats_interval) |
 | 7				| dsl_status             | test_api_dsl_status                 | [7](./api/dsl.md#status)                 |
 | 8				| dsl_stats              | test_api_dsl_stats                  | [8](./api/dsl.md#stats)                  |
+| 9                             | dsl_configure          | test_api_dsl_configure              | [9](./api/dsl.line.md#configure)         |
+| 10                            | dsl_configure_fail     | test_api_dsl_configure_fail         | [9](./api/dsl.line.md#configure)         |
+| 11                            | atm_configure          | test_api_atm_configure              | [15](./api/atm.link.md#configure)        |
+| 12                            | atm_configure_fail     | test_api_atm_configure_fai          | [15](./api/atm.link.md#configure)        |
 
 #### test_api_dsl_line_status
 
@@ -191,3 +218,72 @@ Test the *dslmngr* UBUS API callback `dsl_stats_all()`, providing the method [ds
 
 Parameters' values included in the returned JSON object of the output shall be equal to those returned by the corresponding
 stub API in *libdsl* building for TEST platform.
+
+#### dsl_configure
+
+##### Description
+
+Test the *dslmngr* UBUS API callback `dsl_line_configure()`, providing the method [dsl configure](./api/dsl.line.md#configure).
+
+##### Test Steps
+
+- Prepare valid input parameters
+- Call `dsl_line_configure()`
+- Analyze and verify the output
+
+##### Expected Results
+
+The dumped input parameters by the corresponding libdsl API must be as same as those prepared in th first step. And the
+returned value must indicate success.
+
+#### dsl_configure_fail
+
+##### Description
+
+Test the *dslmngr* UBUS API callback `dsl_line_configure()`, providing the method [dsl configure](./api/dsl.line.md#configure).
+
+##### Test Steps
+
+- Prepare invalid input parameters
+- Call `dsl_line_configure()`
+- Analyze and verify the output
+
+##### Expected Results
+
+The dumped input parameters by the corresponding libdsl API must be as same as those prepared in th first step. And the
+returned value must indicate failure.
+
+#### atm_configure
+
+##### Description
+
+Test the *dslmngr* UBUS API callback `atm_link_configure()`, providing the method [atm configure](./api/atm.link.md#configure).
+
+##### Test Steps
+
+- Prepare valid input parameters
+- Call `atm_link_configure()`
+- Analyze and verify the output
+
+##### Expected Results
+
+The dumped input parameters by the corresponding libdsl API must be as same as those prepared in th first step. And the
+returned value must indicate success.
+
+#### atm_configure_fail
+
+##### Description
+
+Test the *dslmngr* UBUS API callback `atm_link_configure()`, providing the method [atm configure](./api/atm.link.md#configure).
+
+##### Test Steps
+
+- Prepare valid input parameters
+- Call `atm_link_configure()`
+- Analyze and verify the output
+
+##### Expected Results
+
+The dumped input parameters by the corresponding libdsl API must be as same as those prepared in th first step. And the
+returned value must indicate failure.
+
diff --git a/docs/ubus.splash.md b/docs/ubus.splash.md
index 9838817..643fdfd 100644
--- a/docs/ubus.splash.md
+++ b/docs/ubus.splash.md
@@ -3,3 +3,6 @@
 * [DSL](./api/dsl.md)
 * [DSL Line](./api/dsl.line.md)
 * [DSL Channel](./api/dsl.channel.md)
+* [FAST Line](./api/fast.line.md)
+* [ATM Link](./api/atm.link.md)
+* [PTM Link](./api/ptm.link.md)
diff --git a/dslmngr.c b/dslmngr.c
index c08ed74..aa42c2c 100644
--- a/dslmngr.c
+++ b/dslmngr.c
@@ -32,6 +32,7 @@
 #include <uci.h>
 
 #include "xdsl.h"
+#include "xtm.h"
 #include "dslmngr.h"
 
 #define DSL_OBJECT_LINE "line"
@@ -42,6 +43,8 @@ struct value2text {
 	char *text;
 };
 
+int current_log_level = LOG_WARNING;
+
 enum {
 	DSL_STATS_INTERVAL,
 	__DSL_STATS_MAX,
@@ -51,6 +54,25 @@ static const struct blobmsg_policy dsl_stats_policy[__DSL_STATS_MAX] = {
 	[DSL_STATS_INTERVAL] = { .name = "interval", .type = BLOBMSG_TYPE_STRING },
 };
 
+enum {
+	DSL_CONFIGURE_XTSE,
+	DSL_CONFIGURE_VDSL2_PROFILES,
+	DSL_CONFIGURE_FAST_PROFILES,
+	DSL_CONFIGURE_ENABLE_DATA_GATHERING,
+	DSL_CONFIGURE_LIMIT_MASK,
+	DSL_CONFIGURE_US0_MASK,
+	__DSL_CONFIGURE_MAX
+};
+
+static const struct blobmsg_policy dsl_configure_policy[] = {
+	[DSL_CONFIGURE_XTSE] = { .name = "xtse", .type = BLOBMSG_TYPE_STRING },
+	[DSL_CONFIGURE_VDSL2_PROFILES] = { .name = "vdsl2_profiles", .type = BLOBMSG_TYPE_STRING },
+	[DSL_CONFIGURE_FAST_PROFILES] = { .name = "fast_profiles", .type = BLOBMSG_TYPE_STRING },
+	[DSL_CONFIGURE_ENABLE_DATA_GATHERING] = { .name = "data_gathering", .type = BLOBMSG_TYPE_BOOL },
+	[DSL_CONFIGURE_LIMIT_MASK] = { .name = "limit_mask", .type = BLOBMSG_TYPE_INT32 },
+	[DSL_CONFIGURE_US0_MASK] = { .name = "us0_mask", .type = BLOBMSG_TYPE_INT32 }
+};
+
 static const char *dsl_if_status_str(enum itf_status status)
 {
 	switch (status) {
@@ -203,18 +225,22 @@ static const char *dsl_line_encoding_str(enum dsl_line_encoding encoding)
 
 static const char *dsl_profile_str(enum dsl_profile profile)
 {
-	switch (profile) {
-	case VDSL2_8a: return "8a";
-	case VDSL2_8b: return "8b";
-	case VDSL2_8c: return "8c";
-	case VDSL2_8d: return "8d";
-	case VDSL2_12a: return "12a";
-	case VDSL2_12b: return "12b";
-	case VDSL2_17a: return "17a";
-	case VDSL2_30a: return "30a";
-	case VDSL2_35b: return "35b";
-	default: return "unknown";
-	}
+	const char *str = dsl_get_string_value(vdsl2_profiles, profile);
+
+	if (!str)
+		return "unknown";
+
+	return str;
+};
+
+static const char *fast_profile_str(enum fast_profile profile)
+{
+	const char *str = dsl_get_string_value(fast_profiles, profile);
+
+	if (!str)
+		return "unknown";
+
+	return str;
 };
 
 static const char *dsl_power_state_str(enum dsl_power_state power_state)
@@ -656,18 +682,16 @@ __ret:
 	return retval;
 }
 
-static struct ubus_method dsl_main_methods[] = {
+static struct ubus_method dsl_methods[] = {
 	{ .name = "status", .handler = dsl_status_all },
 	{ .name = "stats", .handler = dsl_stats_all }
 };
-
-static struct ubus_object_type dsl_main_type = UBUS_OBJECT_TYPE("dsl", dsl_main_methods);
-
-static struct ubus_object dsl_main_object = {
+static struct ubus_object_type dsl_type = UBUS_OBJECT_TYPE("dsl", dsl_methods);
+static struct ubus_object dsl_object = {
 	.name = "dsl",
-	.type = &dsl_main_type,
-	.methods = dsl_main_methods,
-	.n_methods = ARRAY_SIZE(dsl_main_methods),
+	.type = &dsl_type,
+	.methods = dsl_methods,
+	.n_methods = ARRAY_SIZE(dsl_methods),
 };
 
 int dsl_line_status(struct ubus_context *ctx, struct ubus_object *obj,
@@ -777,7 +801,8 @@ __ret:
 
 static struct ubus_method dsl_line_methods[] = {
 	{ .name = "status", .handler = dsl_line_status },
-	UBUS_METHOD("stats", dsl_line_stats, dsl_stats_policy )
+	UBUS_METHOD("stats", dsl_line_stats, dsl_stats_policy),
+	UBUS_METHOD("configure", dsl_line_configure,  dsl_configure_policy)
 };
 
 static struct ubus_object_type dsl_line_type = UBUS_OBJECT_TYPE("dsl.line", dsl_line_methods);
@@ -894,16 +919,692 @@ static struct ubus_method dsl_channel_methods[] = {
 
 static struct ubus_object_type dsl_channel_type = UBUS_OBJECT_TYPE("dsl.channel", dsl_channel_methods);
 
+static void fast_line_status_to_blob(const struct fast_line *line, struct blob_buf *bb)
+{
+	void *array;
+	int i, j, count;
+	unsigned long opt;
+	char str[64];
+	enum itf_status if_status = line->status;
+
+	/*
+	 * Put most important information at the beginning
+	 */
+	if (if_status == IF_UP && line->link_status != LINK_UP) {
+		/* Some inconsistent status might be retrieved from the driver, i.e. interface status is
+		 * up and the link status is not up. In this case, we force reporting the interface's
+		 * status being down */
+		if_status = IF_DOWN;
+	}
+	blobmsg_add_string(bb, "status", dsl_if_status_str(if_status));
+	blobmsg_add_u8(bb, "upstream", line->upstream);
+	blobmsg_add_string(bb, "firmware_version", line->firmware_version);
+	blobmsg_add_string(bb, "link_status", dsl_link_status_str(line->link_status));
+	blobmsg_add_string(bb, "current_profile", fast_profile_str(line->current_profile));
+	blobmsg_add_string(bb, "power_management_state", dsl_power_state_str(line->power_management_state));
+
+	// max_bit_rate
+	unsigned long rates[] = { line->max_bit_rate.us, line->max_bit_rate.ds };
+	dsl_add_usds_to_blob("max_bit_rate", false, rates, bb);
+
+	// allowed_profiles
+	array = blobmsg_open_array(bb, "allowed_profiles");
+	for (opt = (unsigned long)FAST_106a; opt <= (unsigned long)FAST_212a; opt <<= 1) {
+		if (line->allowed_profiles & opt)
+			blobmsg_add_string(bb, "", fast_profile_str(opt));
+	}
+	blobmsg_close_array(bb, array);
+
+	blobmsg_add_u32(bb, "success_failure_cause", line->success_failure_cause);
+	blobmsg_add_u32(bb, "upbokler", line->upbokler);
+
+	// last_transmitted_signal
+	long signals[] = { line->last_transmitted_signal.us, line->last_transmitted_signal.ds };
+	dsl_add_usds_to_blob("last_transmitted_signal", true, signals, bb);
+
+
+	blobmsg_add_u32(bb, "upbokle", line->upbokle);
+	dsl_add_int_to_blob("line_number", line->line_number, bb);
+
+	// noise_margin
+	long margins[] = { line->noise_margin.us, line->noise_margin.ds };
+	dsl_add_usds_to_blob("noise_margin", true, margins, bb);
+
+	// attenuation
+	long attenuations[] = { line->attenuation.us, line->attenuation.ds };
+	dsl_add_usds_to_blob("attenuation", true, attenuations, bb);
+
+	// power
+	long powers[] = { line->power.us, line->power.ds };
+	dsl_add_usds_to_blob("power", true, powers, bb);
+
+	// snrm_rmc
+	long rmc[] = { line->snrm_rmc.us, line->snrm_rmc.ds };
+	dsl_add_usds_to_blob("snrm_rmc", true, rmc, bb);
+
+	// bits_rmc_ps
+	dsl_add_sequence_to_blob("bits_rmc_ps", false, line->bits_rmc_ps.count,
+				(const long *)line->bits_rmc_ps.elements, bb);
+
+	// fext_to_cancel_enable
+	void *table = blobmsg_open_table(bb, "fext_to_cancel_enable");
+	blobmsg_add_u8(bb, "us", line->fext_to_cancel_enable.us);
+	blobmsg_add_u8(bb, "ds", line->fext_to_cancel_enable.ds);
+	blobmsg_close_table(bb, table);
+
+	// etr
+	unsigned long etr[] = { line->etr.us, line->etr.ds };
+	dsl_add_usds_to_blob("etr", false, etr, bb);
+
+	// att_etr
+	unsigned long att_etr[] = { line->att_etr.us, line->att_etr.ds };
+	dsl_add_usds_to_blob("att_etr", false, att_etr, bb);
+
+	// min_eftr
+	unsigned long min_eftr[] = { line->min_eftr.us, line->min_eftr.ds };
+	dsl_add_usds_to_blob("min_eftr", false, min_eftr, bb);
+}
+
+int fast_line_status(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method, struct blob_attr *msg)
+{
+	static struct blob_buf bb;
+	struct fast_line line;
+	int retval = UBUS_STATUS_OK;
+	int num = -1;
+
+	// Initialize the buffer
+	memset(&bb, 0, sizeof(bb));
+	blob_buf_init(&bb, 0);
+
+	// Get line status
+	sscanf(obj->name, "fast.line.%d", &num);
+	if (xdsl_ops.get_fast_line_info == NULL || (*xdsl_ops.get_fast_line_info)(num, &line) != 0) {
+		retval = UBUS_STATUS_UNKNOWN_ERROR;
+		goto __ret;
+	}
+	fast_line_status_to_blob(&line, &bb);
+
+	// Send the reply
+	ubus_send_reply(ctx, req, bb.head);
+
+__ret:
+	blob_buf_free(&bb);
+	return retval;
+}
+
+static void fast_line_stats_interval_to_blob(const struct fast_line_stats_interval *stats, struct blob_buf *bb)
+{
+	blobmsg_add_u64(bb, "errored_secs", stats->errored_secs);
+	blobmsg_add_u64(bb, "severely_errored_secs", stats->severely_errored_secs);
+	blobmsg_add_u64(bb, "loss", stats->loss);
+	blobmsg_add_u64(bb, "lors", stats->lors);
+	blobmsg_add_u64(bb, "uas", stats->uas);
+	blobmsg_add_u64(bb, "rtx_uc", stats->rtx_uc);
+	blobmsg_add_u64(bb, "rtx_tx", stats->rtx_tx);
+	blobmsg_add_u64(bb, "success_bsw", stats->success_bsw);
+	blobmsg_add_u64(bb, "success_sra", stats->success_sra);
+	blobmsg_add_u64(bb, "success_fra", stats->success_fra);
+	blobmsg_add_u64(bb, "success_rpa", stats->success_rpa);
+	blobmsg_add_u64(bb, "success_tiga", stats->success_tiga);
+}
+
+int fast_line_stats_handler(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method, struct blob_attr *msg)
+{
+	static struct blob_buf bb;
+	struct blob_attr *tb[__DSL_STATS_MAX];
+	enum dsl_stats_type type = DSL_STATS_QUARTERHOUR + 1;
+	struct fast_line_stats stats;
+	struct fast_line_stats_interval stats_interval;
+	int retval = UBUS_STATUS_OK;
+	int num = -1;
+	int i, j;
+	void *table;
+
+	// Parse and validation check the interval type if any
+	blobmsg_parse(dsl_stats_policy, __DSL_STATS_MAX, tb, blob_data(msg), blob_len(msg));
+	if (tb[DSL_STATS_INTERVAL]) {
+		const char *st = blobmsg_data(tb[DSL_STATS_INTERVAL]);
+
+		for (i = 0; i < ARRAY_SIZE(dsl_stats_types); i++) {
+			if (strcasecmp(st, dsl_stats_types[i].text) == 0) {
+				type = dsl_stats_types[i].value;
+				break;
+			}
+		}
+
+		if (i >= ARRAY_SIZE(dsl_stats_types)) {
+			DSLMNGR_LOG(LOG_ERR, "Wrong argument for interval statistics type\n");
+			return UBUS_STATUS_INVALID_ARGUMENT;
+		}
+	}
+
+	// Initialize the buffer
+	memset(&bb, 0, sizeof(bb));
+	blob_buf_init(&bb, 0);
+
+	// Get line number
+	sscanf(obj->name, "fast.line.%d", &num);
+
+	// Get line interval statistics
+	if (type >= DSL_STATS_TOTAL && type <= DSL_STATS_QUARTERHOUR) {
+		if (xdsl_ops.get_fast_line_stats_interval == NULL || (*xdsl_ops.get_fast_line_stats_interval)
+			(num, type, &stats_interval) != 0) {
+			retval = UBUS_STATUS_UNKNOWN_ERROR;
+			goto __ret;
+		}
+		fast_line_stats_interval_to_blob(&stats_interval, &bb);
+	} else {
+		// Get line statistics
+		if (xdsl_ops.get_fast_line_stats == NULL || (*xdsl_ops.get_fast_line_stats)(num, &stats) != 0) {
+			retval = UBUS_STATUS_UNKNOWN_ERROR;
+			goto __ret;
+		}
+		dsl_stats_to_blob(&stats, &bb);
+
+		// Get all interval statistics
+		for (j = 0; j < ARRAY_SIZE(dsl_stats_types); j++) {
+			table = blobmsg_open_table(&bb, dsl_stats_types[j].text);
+
+			if (xdsl_ops.get_fast_line_stats_interval == NULL || (*xdsl_ops.get_fast_line_stats_interval)
+				(num, dsl_stats_types[j].value, &stats_interval) != 0) {
+				retval = UBUS_STATUS_UNKNOWN_ERROR;
+				goto __ret;
+			}
+			fast_line_stats_interval_to_blob(&stats_interval, &bb);
+
+			blobmsg_close_table(&bb, table);
+		}
+	}
+
+	// Send the reply
+	ubus_send_reply(ctx, req, bb.head);
+
+__ret:
+	blob_buf_free(&bb);
+	return retval;
+}
+
+static struct ubus_method fast_line_methods[] = {
+	{ .name = "status", .handler = fast_line_status },
+	UBUS_METHOD("stats", fast_line_stats_handler, dsl_stats_policy )
+};
+
+static struct ubus_object_type fast_line_type = UBUS_OBJECT_TYPE("fast.line", fast_line_methods);
+
+static const char *atm_link_type_str(enum atm_link_type type)
+{
+	const char *str = dsl_get_string_value(atm_link_types, type);
+
+	if (!str)
+		return "unknown";
+
+	return str;
+}
+
+static const char *atm_encap_str(enum atm_encapsulation encap)
+{
+	const char *str = dsl_get_string_value(atm_encapsulations, encap);
+
+	if (!str)
+		return "unknown";
+
+	return str;
+}
+
+static const char *atm_aal_str(enum atm_aal aal)
+{
+	switch (aal) {
+	case ATM_AAL1: return "aal1";
+	case ATM_AAL2: return "aal2";
+	case ATM_AAL3: return "aal3";
+	case ATM_AAL4: return "aal4";
+	case ATM_AAL5: return "aal5";
+	default: return "unknown";
+	}
+}
+
+static const char *atm_qos_class_str(enum atm_qos_class class)
+{
+	const char *str = dsl_get_string_value(atm_qos_classes, class);
+
+	if (!str)
+		return "unknown";
+
+	return str;
+}
+
+static void atm_add_dest_addr_to_blob(const struct atm_dest_addr *dest_addr, struct blob_buf *bb)
+{
+	void *table = blobmsg_open_table(bb, "dest_addr");
+	blobmsg_add_u32(bb, "vpi", dest_addr->vpi);
+	blobmsg_add_u32(bb, "vci", dest_addr->vci);
+	blobmsg_close_table(bb, table);
+}
+
+static void atm_link_status_to_blob(const struct atm_link *link, const struct atm_link_qos *qos, struct blob_buf *bb)
+{
+	void *array, *table;
+	int i;
+
+	blobmsg_add_string(bb, "status", dsl_if_status_str(link->status));
+	blobmsg_add_string(bb, "link_type", atm_link_type_str(link->link_type));
+	blobmsg_add_u8(bb, "auto_config", link->auto_config);
+	atm_add_dest_addr_to_blob(&link->dest_addr, bb);
+	blobmsg_add_string(bb, "encapsulation", atm_encap_str(link->encapsulation));
+	blobmsg_add_u8(bb, "fcs_preserved", link->fcs_preserved);
+
+	array = blobmsg_open_array(bb, "vc_search_list");
+	for (i = 0; i < link->vc_list_count; i++) {
+		atm_add_dest_addr_to_blob(&link->vc_search_list[i], bb);
+	}
+	blobmsg_close_array(bb, array);
+
+	blobmsg_add_string(bb, "aal", atm_aal_str(link->aal));
+
+	table = blobmsg_open_table(bb, "qos");
+	blobmsg_add_string(bb, "qos_class", atm_qos_class_str(qos->qos_class));
+	blobmsg_add_u32(bb, "peak_cell_rate", qos->peak_cell_rate);
+	blobmsg_add_u32(bb, "max_burst_size", qos->max_burst_size);
+	blobmsg_add_u32(bb, "sustainable_cell_rate", qos->sustainable_cell_rate);
+	blobmsg_close_table(bb, table);
+}
+
+int atm_link_status(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method, struct blob_attr *msg)
+{
+	static struct blob_buf bb;
+	struct atm_link link;
+	struct atm_link_qos qos;
+	int retval = UBUS_STATUS_OK;
+	int num = -1;
+
+	// Initialize the buffer
+	memset(&bb, 0, sizeof(bb));
+	blob_buf_init(&bb, 0);
+
+	// Get line status
+	sscanf(obj->name, "atm.link.%d", &num);
+	if (atm_funcs.get_link_info == NULL || (*atm_funcs.get_link_info)(num, &link, &qos) != 0) {
+		retval = UBUS_STATUS_UNKNOWN_ERROR;
+		goto __ret;
+	}
+	atm_link_status_to_blob(&link, &qos, &bb);
+
+	// Send the reply
+	ubus_send_reply(ctx, req, bb.head);
+
+__ret:
+	blob_buf_free(&bb);
+	return retval;
+}
+
+static void atm_link_stats_to_blob(const struct atm_link_stats *stats, struct blob_buf *bb)
+{
+	blobmsg_add_u64(bb, "transmitted_blocks", stats->transmitted_blocks);
+	blobmsg_add_u64(bb, "received_blocks", stats->received_blocks);
+	blobmsg_add_u64(bb, "crc_errors", stats->crc_errors);
+}
+
+int atm_link_stats_handler(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method, struct blob_attr *msg)
+{
+	static struct blob_buf bb;
+	struct atm_link_stats stats;
+	int retval = UBUS_STATUS_OK;
+	int num = -1;
+
+	// Initialize the buffer
+	memset(&bb, 0, sizeof(bb));
+	blob_buf_init(&bb, 0);
+
+	// Get line status
+	sscanf(obj->name, "atm.link.%d", &num);
+	if (atm_funcs.get_link_stats == NULL || (*atm_funcs.get_link_stats)(num, &stats) != 0) {
+		retval = UBUS_STATUS_UNKNOWN_ERROR;
+		goto __ret;
+	}
+	atm_link_stats_to_blob(&stats, &bb);
+
+	// Send the reply
+	ubus_send_reply(ctx, req, bb.head);
+
+
+__ret:
+	blob_buf_free(&bb);
+	return retval;
+}
+
+enum {
+	ATM_CONFIGURE_LINK_TYPE,
+	ATM_CONFIGURE_VPI,
+	ATM_CONFIGURE_VCI,
+	ATM_CONFIGURE_ENCAPSULATION,
+	ATM_CONFIGURE_QOS_CLASS,
+	ATM_CONFIGURE_PEAK_CELL_RATE,
+	ATM_CONFIGURE_MAX_BURST_SIZE,
+	ATM_CONFIGURE_SUSTAINABLE_CELL_RATE,
+	__ATM_CONFIGURE_MAX
+};
+
+static const struct blobmsg_policy atm_configure_policy[] = {
+	[ATM_CONFIGURE_LINK_TYPE] = { .name = "link_type", .type = BLOBMSG_TYPE_STRING },
+	[ATM_CONFIGURE_VPI] = { .name = "vpi", .type = BLOBMSG_TYPE_INT32 },
+	[ATM_CONFIGURE_VCI] = { .name = "vci", .type = BLOBMSG_TYPE_INT32 },
+	[ATM_CONFIGURE_ENCAPSULATION] = { .name = "encapsulation", .type = BLOBMSG_TYPE_STRING },
+	[ATM_CONFIGURE_QOS_CLASS] = { .name = "qos_class", .type = BLOBMSG_TYPE_STRING },
+	[ATM_CONFIGURE_PEAK_CELL_RATE] = { .name = "peak_cell_rate", .type = BLOBMSG_TYPE_INT32 },
+	[ATM_CONFIGURE_MAX_BURST_SIZE] = { .name = "max_burst_size", .type = BLOBMSG_TYPE_INT32 },
+	[ATM_CONFIGURE_SUSTAINABLE_CELL_RATE] = { .name = "sustainable_cell_rate", .type = BLOBMSG_TYPE_INT32 }
+};
+
+/**
+ * Handler of UBUS call "atm.link.x configure"
+ *
+ * Calling example
+ *
+ * ubus call atm.link.0 configure "{'link_type':'eoa','vpi':8,'vci':35,'encapsulation':'llc',\
+    'qos_class':'ubr','peak_cell_rate':10240,'max_burst_size':8124,'sustainable_cell_rate':9600}"
+ *
+ */
+int atm_link_configure(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method, struct blob_attr *msg)
+{
+	struct blob_attr *tb[__ATM_CONFIGURE_MAX];
+	struct atm_link link;
+	struct atm_link_qos qos;
+	int link_num = -1;
+
+	memset(&link, 0, sizeof(link));
+	memset(&qos, 0, sizeof(qos));
+
+	// Parse and validation check the input parameters
+	if (blobmsg_parse(atm_configure_policy, __ATM_CONFIGURE_MAX, tb, blob_data(msg), blob_len(msg)) != 0)
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	// link_type
+	if (tb[ATM_CONFIGURE_LINK_TYPE]) {
+		const char *type_str = blobmsg_data(tb[ATM_CONFIGURE_LINK_TYPE]);
+		int type_enum = dsl_get_enum_value(atm_link_types, type_str);
+
+		if (type_enum == -1) {
+			DSLMNGR_LOG(LOG_ERR,
+				"Wrong argument for link_type. It should be like \"eoa\"\n");
+			return UBUS_STATUS_INVALID_ARGUMENT;
+		}
+		link.link_type = type_enum;
+
+		DSLMNGR_LOG(LOG_DEBUG, "link.link_type = %d(%s)\n", link.link_type, type_str);
+	}
+
+	// vpi & vci
+	if (tb[ATM_CONFIGURE_VPI] && tb[ATM_CONFIGURE_VCI]) {
+		link.dest_addr.vpi = blobmsg_get_u32(tb[ATM_CONFIGURE_VPI]);
+		link.dest_addr.vci = blobmsg_get_u32(tb[ATM_CONFIGURE_VCI]);
+
+		DSLMNGR_LOG(LOG_DEBUG, "link.dest_addr = { %d, %d }\n", link.dest_addr.vpi, link.dest_addr.vci);
+	}
+
+	// encapsulation
+	if (tb[ATM_CONFIGURE_ENCAPSULATION]) {
+		const char *encap_str = blobmsg_data(tb[ATM_CONFIGURE_ENCAPSULATION]);
+		int encap_enum = dsl_get_enum_value(atm_encapsulations, encap_str);
+
+		if (encap_enum == -1) {
+			DSLMNGR_LOG(LOG_ERR,
+				"Wrong argument for encapsulation. It should be like \"llc\"\n");
+			return UBUS_STATUS_INVALID_ARGUMENT;
+		}
+		link.encapsulation = encap_enum;
+
+		DSLMNGR_LOG(LOG_DEBUG, "link.encapsulation = %d(%s)\n", link.encapsulation, encap_str);
+	}
+
+	// qos_class
+	if (tb[ATM_CONFIGURE_QOS_CLASS]) {
+		const char *class_str = blobmsg_data(tb[ATM_CONFIGURE_QOS_CLASS]);
+		int class_enum = dsl_get_enum_value(atm_qos_classes, class_str);
+
+		if (class_enum == -1) {
+			DSLMNGR_LOG(LOG_ERR,
+				"Wrong argument for qos_class. It should be like \"ubr\"\n");
+			return UBUS_STATUS_INVALID_ARGUMENT;
+		}
+		qos.qos_class = class_enum;
+
+		DSLMNGR_LOG(LOG_DEBUG, "qos.qos_class = %d(%s)\n", qos.qos_class, class_str);
+	}
+
+	// peak_cell_rate
+	if (tb[ATM_CONFIGURE_PEAK_CELL_RATE]) {
+		qos.peak_cell_rate = blobmsg_get_u32(tb[ATM_CONFIGURE_PEAK_CELL_RATE]);
+
+		DSLMNGR_LOG(LOG_DEBUG, "qos.peak_cell_rate = %u\n", qos.peak_cell_rate);
+	}
+
+	// max_burst_size
+	if (tb[ATM_CONFIGURE_MAX_BURST_SIZE]) {
+		qos.max_burst_size = blobmsg_get_u32(tb[ATM_CONFIGURE_MAX_BURST_SIZE]);
+
+		DSLMNGR_LOG(LOG_DEBUG, "qos.max_burst_size = %u\n", qos.max_burst_size);
+	}
+
+	// sustainable_cell_rate
+	if (tb[ATM_CONFIGURE_SUSTAINABLE_CELL_RATE]) {
+		qos.sustainable_cell_rate = blobmsg_get_u32(tb[ATM_CONFIGURE_SUSTAINABLE_CELL_RATE]);
+
+		DSLMNGR_LOG(LOG_DEBUG, "qos.sustainable_cell_rate = %u\n", qos.sustainable_cell_rate);
+	}
+
+	// Get link number and call libdsl API
+	sscanf(obj->name, "atm.link.%d", &link_num);
+	if (atm_funcs.configure(link_num, &link, &qos) != 0) {
+		DSLMNGR_LOG(LOG_ERR, "ATM link %d confuration failed\n", link_num);
+		return UBUS_STATUS_UNKNOWN_ERROR;
+	}
+
+	return 0;
+}
+
+static struct ubus_method atm_link_methods[] = {
+	{ .name = "status", .handler = atm_link_status },
+	{ .name = "stats",  .handler = atm_link_stats_handler },
+	UBUS_METHOD("configure", atm_link_configure,  atm_configure_policy)
+};
+
+static struct ubus_object_type atm_link_type = UBUS_OBJECT_TYPE("atm.link", atm_link_methods);
+
+static void ptm_link_status_to_blob(const struct ptm_link *link, struct blob_buf *bb)
+{
+	char mac[18];
+
+	blobmsg_add_string(bb, "status", dsl_if_status_str(link->status));
+	snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x",
+			link->mac_addr[0], link->mac_addr[1], link->mac_addr[2],
+			link->mac_addr[3], link->mac_addr[4], link->mac_addr[5]);
+	blobmsg_add_string(bb, "mac_addr", mac);
+}
+
+int ptm_link_status(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method, struct blob_attr *msg)
+{
+	static struct blob_buf bb;
+	struct ptm_link link;
+	int retval = UBUS_STATUS_OK;
+	int num = -1;
+
+	// Initialize the buffer
+	memset(&bb, 0, sizeof(bb));
+	blob_buf_init(&bb, 0);
+
+	// Get line status
+	sscanf(obj->name, "ptm.link.%d", &num);
+	if (ptm_funcs.get_link_info == NULL || (*ptm_funcs.get_link_info)(num, &link) != 0) {
+		retval = UBUS_STATUS_UNKNOWN_ERROR;
+		goto __ret;
+	}
+	ptm_link_status_to_blob(&link, &bb);
+
+	// Send the reply
+	ubus_send_reply(ctx, req, bb.head);
+
+__ret:
+	blob_buf_free(&bb);
+	return retval;
+}
+
+static struct ubus_method ptm_link_methods[] = {
+	{ .name = "status", .handler = ptm_link_status }
+};
+
+static struct ubus_object_type ptm_link_type = UBUS_OBJECT_TYPE("ptm.link", ptm_link_methods);
+
+/**
+ * Handler of UBUS call "dsl.line.x configure"
+ *
+ * Calling example
+ *
+ * ubus call dsl.line.0 configure "{'xtse':'01,02,ab,cd,41,52,f8,6e',\
+    'vdsl2_profiles':'8a,8b,8c,8d,17a,30a,35b',\
+    'fast_profiles':'106a,212a','data_gathering':true,'limit_mask':255,'us0_mask':511}"
+ *
+ */
+int dsl_line_configure(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method, struct blob_attr *msg)
+{
+	struct blob_attr *tb[__DSL_CONFIGURE_MAX];
+	struct dsl_config_params cfg_params;
+	int line_num = -1, i;
+
+	if (!xdsl_ops.configure) {
+		DSLMNGR_LOG(LOG_ERR, "This function is not supported on the current platform yet\n");
+		return UBUS_STATUS_NOT_SUPPORTED;
+	}
+
+	memset(&cfg_params, 0, sizeof(cfg_params));
+
+	// Parse and validation check the input parameters
+	if (blobmsg_parse(dsl_configure_policy, __DSL_CONFIGURE_MAX, tb, blob_data(msg), blob_len(msg)) != 0)
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	// XTSE
+	if (tb[DSL_CONFIGURE_XTSE]) {
+		const char *xtse_str = blobmsg_data(tb[DSL_CONFIGURE_XTSE]);
+
+		if (!xtse_str || !*xtse_str ||
+			sscanf(xtse_str, "%02hhx,%02hhx,%02hhx,%02hhx,%02hhx,%02hhx,%02hhx,%02hhx",
+					&cfg_params.xtse[0],&cfg_params.xtse[1],&cfg_params.xtse[2],&cfg_params.xtse[3],
+					&cfg_params.xtse[4],&cfg_params.xtse[5],&cfg_params.xtse[6],&cfg_params.xtse[7]) != 8) {
+			DSLMNGR_LOG(LOG_ERR, "Wrong argument for xtse. It should be like \"01,02,ab,cd,41,52,f8,6e\"\n");
+			return UBUS_STATUS_INVALID_ARGUMENT;
+		}
+		DSLMNGR_LOG(LOG_DEBUG, "cfg_params.xtse = {%02hhx,%02hhx,%02hhx,%02hhx,%02hhx,%02hhx,%02hhx,%02hhx}\n",
+				cfg_params.xtse[0],cfg_params.xtse[1],cfg_params.xtse[2],cfg_params.xtse[3],
+				cfg_params.xtse[4],cfg_params.xtse[5],cfg_params.xtse[6],cfg_params.xtse[7]);
+	}
+
+	// VDSL2 profiles
+	if (tb[DSL_CONFIGURE_VDSL2_PROFILES]) {
+		const char *profiles_str = blobmsg_data(tb[DSL_CONFIGURE_VDSL2_PROFILES]);
+		char *dup, *token, *saveptr;
+
+		if (!profiles_str || !*profiles_str) {
+			DSLMNGR_LOG(LOG_ERR,
+				"Wrong argument for vdsl2_profiles. It should be like \"8a,8b,8c,8d,17a,30a,35b\"\n");
+			return UBUS_STATUS_INVALID_ARGUMENT;
+		}
+
+		dup = alloca(strlen(profiles_str) + 1);
+		strcpy(dup, profiles_str);
+		for (token = strtok_r(dup, ",", &saveptr); token != NULL; token = strtok_r(NULL, ",", &saveptr)) {
+			int profile = dsl_get_enum_value(vdsl2_profiles, token);
+			if (profile != -1)
+				cfg_params.vdsl2_profiles |= profile;
+			else {
+				DSLMNGR_LOG(LOG_ERR,
+					"Wrong argument for vdsl2_profiles. It should be like \"8a,8b,8c,8d,17a,30a,35b\"\n");
+				return UBUS_STATUS_INVALID_ARGUMENT;
+			}
+		}
+
+		DSLMNGR_LOG(LOG_DEBUG, "cfg_params.vdsl2_profiles = 0x%lx(%s)\n", cfg_params.vdsl2_profiles, profiles_str);
+	}
+
+	// FAST profiles
+	if (tb[DSL_CONFIGURE_FAST_PROFILES]) {
+		const char *profiles_str = blobmsg_data(tb[DSL_CONFIGURE_FAST_PROFILES]);
+		char *dup, *token, *saveptr;
+
+		if (!profiles_str || !*profiles_str) {
+			DSLMNGR_LOG(LOG_ERR,
+				"Wrong argument for fast_profiles. It should be like \"106a,212a\"\n");
+			return UBUS_STATUS_INVALID_ARGUMENT;
+		}
+
+		dup = alloca(strlen(profiles_str) + 1);
+		strcpy(dup, profiles_str);
+		for (token = strtok_r(dup, ",", &saveptr); token != NULL; token = strtok_r(NULL, ",", &saveptr)) {
+			int profile = dsl_get_enum_value(fast_profiles, token);
+			if (profile != -1)
+				cfg_params.fast_profiles |= profile;
+			else {
+				DSLMNGR_LOG(LOG_ERR,
+					"Wrong argument for fast_profiles. It should be like \"106a,212a\"\n");
+				return UBUS_STATUS_INVALID_ARGUMENT;
+			}
+		}
+
+		DSLMNGR_LOG(LOG_DEBUG, "cfg_params.fast_profiles = 0x%lx(%s)\n", cfg_params.fast_profiles, profiles_str);
+	}
+
+	// enable_data_gathering
+	if (tb[DSL_CONFIGURE_ENABLE_DATA_GATHERING]) {
+		uint8_t enabled = blobmsg_get_u8(tb[DSL_CONFIGURE_ENABLE_DATA_GATHERING]);
+
+		cfg_params.enable_data_gathering = !!enabled;
+
+		DSLMNGR_LOG(LOG_DEBUG, "cfg_params.enable_data_gathering = %d\n", cfg_params.enable_data_gathering);
+	}
+
+	// limit_mask
+	if (tb[DSL_CONFIGURE_LIMIT_MASK]) {
+		cfg_params.limit_mask = blobmsg_get_u32(tb[DSL_CONFIGURE_LIMIT_MASK]);
+
+		DSLMNGR_LOG(LOG_DEBUG, "cfg_params.limit_mask = 0x%x\n", cfg_params.limit_mask);
+	}
+
+	// limit_mask
+	if (tb[DSL_CONFIGURE_US0_MASK]) {
+		cfg_params.us0_mask = blobmsg_get_u32(tb[DSL_CONFIGURE_US0_MASK]);
+
+		DSLMNGR_LOG(LOG_DEBUG, "cfg_params.us0_mask = 0x%x\n", cfg_params.us0_mask);
+	}
+
+	// Get line number
+	sscanf(obj->name, "dsl.line.%d", &line_num);
+	if (xdsl_ops.configure(line_num, &cfg_params) != 0) {
+		DSLMNGR_LOG(LOG_ERR, "DSL line %d confuration failed\n", line_num);
+		return UBUS_STATUS_UNKNOWN_ERROR;
+	}
+
+	return 0;
+}
+
 int dsl_add_ubus_objects(struct ubus_context *ctx)
 {
 	struct ubus_object *line_objects = NULL;
 	struct ubus_object *channel_objects = NULL;
+	struct ubus_object *fast_objects = NULL;
+	struct ubus_object *atm_objects = NULL;
+	struct ubus_object *ptm_objects = NULL;
 	int ret, max_line, max_channel, i;
 
-	ret = ubus_add_object(ctx, &dsl_main_object);
+	// Add objects dsl
+	ret = ubus_add_object(ctx, &dsl_object);
 	if (ret) {
 		DSLMNGR_LOG(LOG_ERR, "Failed to add UBUS object '%s', %s\n",
-				dsl_main_object.name, ubus_strerror(ret));
+				dsl_object.name, ubus_strerror(ret));
 		return -1;
 	}
 
@@ -963,6 +1664,94 @@ int dsl_add_ubus_objects(struct ubus_context *ctx)
 		}
 	}
 
+	// Add objects fast.line.x if supported
+	if (xdsl_ops.get_fast_line_info && xdsl_ops.get_fast_line_stats &&
+		xdsl_ops.get_fast_line_stats_interval) {
+		fast_objects = calloc(max_line, sizeof(struct ubus_object));
+		if (!fast_objects) {
+			DSLMNGR_LOG(LOG_ERR, "Out of memory\n");
+			return -1;
+		}
+		for (i = 0; i < max_line; i++) {
+			char *obj_name;
+
+			if (asprintf(&obj_name, "fast.line.%d", i) <= 0) {
+				DSLMNGR_LOG(LOG_ERR, "Out of memory\n");
+				return -1;
+			}
+
+			fast_objects[i].name = obj_name;
+			fast_objects[i].type = &fast_line_type;
+			fast_objects[i].methods = fast_line_methods;
+			fast_objects[i].n_methods = ARRAY_SIZE(fast_line_methods);
+
+			ret = ubus_add_object(ctx, &fast_objects[i]);
+			if (ret) {
+				DSLMNGR_LOG(LOG_ERR, "Failed to add UBUS object '%s', %s\n",
+						obj_name, ubus_strerror(ret));
+				return -1;
+			}
+		}
+	}
+
+	// Add objects atm.link.x if supported
+	if (atm_funcs.get_link_info && atm_funcs.get_link_stats && atm_funcs.configure) {
+		atm_objects = calloc(max_line, sizeof(struct ubus_object));
+		if (!atm_objects) {
+			DSLMNGR_LOG(LOG_ERR, "Out of memory\n");
+			return -1;
+		}
+		for (i = 0; i < max_line; i++) {
+			char *obj_name;
+
+			if (asprintf(&obj_name, "atm.link.%d", i) <= 0) {
+				DSLMNGR_LOG(LOG_ERR, "Out of memory\n");
+				return -1;
+			}
+
+			atm_objects[i].name = obj_name;
+			atm_objects[i].type = &atm_link_type;
+			atm_objects[i].methods = atm_link_methods;
+			atm_objects[i].n_methods = ARRAY_SIZE(atm_link_methods);
+
+			ret = ubus_add_object(ctx, &atm_objects[i]);
+			if (ret) {
+				DSLMNGR_LOG(LOG_ERR, "Failed to add UBUS object '%s', %s\n",
+						obj_name, ubus_strerror(ret));
+				return -1;
+			}
+		}
+	}
+
+	// Add objects ptm.link.x if supported
+	if (ptm_funcs.get_link_info) {
+		ptm_objects = calloc(max_line, sizeof(struct ubus_object));
+		if (!ptm_objects) {
+			DSLMNGR_LOG(LOG_ERR, "Out of memory\n");
+			return -1;
+		}
+		for (i = 0; i < max_line; i++) {
+			char *obj_name;
+
+			if (asprintf(&obj_name, "ptm.link.%d", i) <= 0) {
+				DSLMNGR_LOG(LOG_ERR, "Out of memory\n");
+				return -1;
+			}
+
+			ptm_objects[i].name = obj_name;
+			ptm_objects[i].type = &ptm_link_type;
+			ptm_objects[i].methods = ptm_link_methods;
+			ptm_objects[i].n_methods = ARRAY_SIZE(ptm_link_methods);
+
+			ret = ubus_add_object(ctx, &ptm_objects[i]);
+			if (ret) {
+				DSLMNGR_LOG(LOG_ERR, "Failed to add UBUS object '%s', %s\n",
+						obj_name, ubus_strerror(ret));
+				return -1;
+			}
+		}
+	}
+
 	// Returns on success
 	return 0;
 
@@ -971,5 +1760,12 @@ __error_ret:
 		free(line_objects);
 	if (channel_objects)
 		free(channel_objects);
+	if (fast_objects)
+		free(fast_objects);
+	if (atm_objects)
+		free(atm_objects);
+	if (ptm_objects)
+		free(ptm_objects);
+
 	return -1;
 }
diff --git a/dslmngr.h b/dslmngr.h
index 56a1136..358c487 100644
--- a/dslmngr.h
+++ b/dslmngr.h
@@ -31,7 +31,12 @@ extern "C" {
 #include <syslog.h>
 #include <libubus.h>
 
-#define DSLMNGR_LOG(log_level, format...) fprintf(stderr, ##format)
+extern int current_log_level;
+
+#define DSLMNGR_LOG(log_level, format...) do { \
+		if (log_level <= current_log_level) \
+			fprintf(stderr, ##format); \
+	} while(0)
 
 #define CHECK_POINT() printf("Check point at %s@%s:%d\n", __func__, __FILE__, __LINE__)
 
@@ -50,6 +55,22 @@ int dsl_channel_stats(struct ubus_context *ctx, struct ubus_object *obj,
 		struct ubus_request_data *req, const char *method, struct blob_attr *msg);
 int dsl_stats_all(struct ubus_context *ctx, struct ubus_object *obj,
 		struct ubus_request_data *req, const char *method, struct blob_attr *msg);
+int fast_line_status(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method, struct blob_attr *msg);
+int fast_line_stats_handler(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method, struct blob_attr *msg);
+
+int atm_link_status(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method, struct blob_attr *msg);
+int atm_link_stats_handler(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method, struct blob_attr *msg);
+int ptm_link_status(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method, struct blob_attr *msg);
+
+int dsl_line_configure(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method, struct blob_attr *msg);
+int atm_link_configure(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method, struct blob_attr *msg);
 
 #ifdef __cplusplus
 }
diff --git a/schemas/ubus/atm.link_stats.json b/schemas/ubus/atm.link_stats.json
new file mode 100644
index 0000000..3fd822f
--- /dev/null
+++ b/schemas/ubus/atm.link_stats.json
@@ -0,0 +1,32 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "$id": "https://www.iopsys.eu/atm.link_stats.json",
+  "title": "JSON Schema for the output of the UBUS call: atm.link.x stats",
+
+  "definitions": {
+    "stats_counter": {
+      "type": "integer",
+      "minimum": 0,
+      "maximum": 4294967295
+    },
+
+    "atm_link_stats": {
+      "$id": "#atm_link_stats",
+      "type": "object",
+
+      "properties" : {
+      	"transmitted_blocks": { "$ref": "#/definitions/stats_counter" },
+      	"received_blocks": { "$ref": "#/definitions/stats_counter" },
+      	"crc_errors": { "$ref": "#/definitions/stats_counter" }
+      },
+
+      "required": [
+        "transmitted_blocks",
+        "received_blocks",
+        "crc_errors"
+      ]
+    }
+  },
+
+  "$ref": "#atm_link_stats"
+}
diff --git a/schemas/ubus/atm.link_status.json b/schemas/ubus/atm.link_status.json
new file mode 100644
index 0000000..e71322f
--- /dev/null
+++ b/schemas/ubus/atm.link_status.json
@@ -0,0 +1,82 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "$id": "https://www.iopsys.eu/atm.link_status.json",
+  "title": "JSON Schema for the output of the UBUS call: atm.link.x status",
+
+  "definitions": {
+
+    "if_status": {
+      "type": "string",
+      "enum": [ "up", "down", "dormant", "not_present", "lower_layer_down", "error", "unknown" ]
+    },
+
+    "atm_link_type": {
+      "type": "string",
+      "enum": [ "unconfigured", "eoa", "ipoa", "pppoa", "cip", "unknown" ]
+    },
+
+    "destination": {
+      "type": "object",
+      "properties": {
+        "vpi": { "type": "integer" },
+        "vci": { "type": "integer" }
+      },
+      "required": [ "vpi", "vci" ]
+    },
+
+    "atm_link_status": {
+      "$id": "#atm_link_status",
+      "type": "object",
+
+      "properties" : {
+
+        "status": { "$ref": "#/definitions/if_status" },
+        "link_type": { "$ref": "#/definitions/atm_link_type" },
+        "auto_config": {"type": "boolean" },
+        "dest_addr": { "$ref": "#/definitions/destination" },
+        "encapsulation": {
+          "type": "string",
+          "enum": [ "llc", "vcmux" ]
+        },
+        "fcs_preserved": { "type": "boolean" },
+        "vc_search_list": {
+          "type": "array",
+          "minItems": 0,
+          "Items": { "$ref": "#/definitions/destination" }
+        },
+        "aal": {
+          "type": "string",
+          "enum": [ "aal1", "aal2", "aal3", "aal4", "aal5" ]
+        },
+
+        "qos": {
+          "type": "object",
+          "properties": {
+            "qos_class": {
+              "type": "string",
+              "enum": [ "ubr", "cbr", "gfr", "vbr_nrt", "vbr_rt", "ubr_plus", "abr" ]
+            },
+            "peak_cell_rate": { "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeInteger" },
+            "max_burst_size": { "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeInteger" },
+            "sustainable_cell_rate": { "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeInteger" }
+          },
+          "required": [ "qos_class", "peak_cell_rate", "max_burst_size", "sustainable_cell_rate" ]
+        }
+      },
+
+      "required": [
+        "status",
+        "link_type",
+        "auto_config",
+        "dest_addr",
+        "encapsulation",
+        "fcs_preserved",
+        "vc_search_list",
+        "aal",
+        "qos"
+      ]
+    }
+  },
+
+  "$ref": "#atm_link_status"
+}
diff --git a/schemas/ubus/fast.line_stats.json b/schemas/ubus/fast.line_stats.json
new file mode 100644
index 0000000..62a3517
--- /dev/null
+++ b/schemas/ubus/fast.line_stats.json
@@ -0,0 +1,82 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "$id": "https://www.iopsys.eu/fast.line_stats.json",
+  "title": "JSON Schema for the output of the UBUS call: fast.line.x stats",
+
+  "definitions": {
+    "stats_counter": {
+      "type": "integer",
+      "minimum": 0,
+      "maximum": 4294967295
+    },
+
+    "line_stats_interval" : {
+      "$id": "#line_stats_interval",
+      "type": "object",
+
+      "properties": {
+        "errored_secs": { "$ref": "#/definitions/stats_counter" },
+        "severely_errored_secs": { "$ref": "#/definitions/stats_counter" },
+        "loss": { "$ref": "#/definitions/stats_counter" },
+        "lors": { "$ref": "#/definitions/stats_counter" },
+        "uas": { "$ref": "#/definitions/stats_counter" },
+        "rtx_uc": { "$ref": "#/definitions/stats_counter" },
+        "rtx_tx": { "$ref": "#/definitions/stats_counter" },
+        "success_bsw": { "$ref": "#/definitions/stats_counter" },
+        "success_sra": { "$ref": "#/definitions/stats_counter" },
+        "success_fra": { "$ref": "#/definitions/stats_counter" },
+        "success_rpa": { "$ref": "#/definitions/stats_counter" },
+        "success_tiga": { "$ref": "#/definitions/stats_counter" }
+      },
+
+      "required": [
+        "errored_secs",
+        "severely_errored_secs",
+        "loss",
+        "lors",
+        "uas",
+        "rtx_uc",
+        "rtx_tx",
+        "success_bsw",
+        "success_sra",
+        "success_fra",
+        "success_rpa",
+        "success_tiga"
+      ]
+    },
+
+    "line_stats": {
+      "$id": "#line_stats",
+      "type": "object",
+
+      "properties" : {
+        "total_start": { "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeInteger" },
+      	"showtime_start": { "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeInteger" },
+      	"last_showtime_start": { "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeInteger" },
+      	"current_day_start": { "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeInteger" },
+      	"quarter_hour_start": { "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeInteger" },
+
+      	"total": { "$ref": "#line_stats_interval" },
+      	"showtime": { "$ref": "#line_stats_interval" },
+      	"lastshowtime": { "$ref": "#line_stats_interval" },
+      	"currentday": { "$ref": "#line_stats_interval" },
+      	"quarterhour": { "$ref": "#line_stats_interval" }
+      },
+
+      "required": [
+        "total_start",
+        "showtime_start",
+        "last_showtime_start",
+        "current_day_start",
+        "quarter_hour_start",
+        "total",
+        "showtime",
+        "lastshowtime",
+        "currentday",
+        "quarterhour"
+      ]
+    }
+  },
+
+  "$ref": "#line_stats"
+}
diff --git a/schemas/ubus/fast.line_stats_interval.json b/schemas/ubus/fast.line_stats_interval.json
new file mode 100644
index 0000000..77ff0bb
--- /dev/null
+++ b/schemas/ubus/fast.line_stats_interval.json
@@ -0,0 +1,50 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "$id": "https://www.iopsys.eu/fast.line_stats_interval.json",
+  "title": "JSON Schema for the output of the UBUS call: fast.line.x stats <interval>",
+
+  "definitions": {
+    "stats_counter": {
+      "type": "integer",
+      "minimum": 0,
+      "maximum": 4294967295
+    },
+
+    "line_stats_interval" : {
+      "$id": "#line_stats_interval",
+      "type": "object",
+
+      "properties": {
+        "errored_secs": { "$ref": "#/definitions/stats_counter" },
+        "severely_errored_secs": { "$ref": "#/definitions/stats_counter" },
+        "loss": { "$ref": "#/definitions/stats_counter" },
+        "lors": { "$ref": "#/definitions/stats_counter" },
+        "uas": { "$ref": "#/definitions/stats_counter" },
+        "rtx_uc": { "$ref": "#/definitions/stats_counter" },
+        "rtx_tx": { "$ref": "#/definitions/stats_counter" },
+        "success_bsw": { "$ref": "#/definitions/stats_counter" },
+        "success_sra": { "$ref": "#/definitions/stats_counter" },
+        "success_fra": { "$ref": "#/definitions/stats_counter" },
+        "success_rpa": { "$ref": "#/definitions/stats_counter" },
+        "success_tiga": { "$ref": "#/definitions/stats_counter" }
+      },
+
+      "required": [
+        "errored_secs",
+        "severely_errored_secs",
+        "loss",
+        "lors",
+        "uas",
+        "rtx_uc",
+        "rtx_tx",
+        "success_bsw",
+        "success_sra",
+        "success_fra",
+        "success_rpa",
+        "success_tiga"
+      ]
+    }
+  },
+
+  "$ref": "#line_stats_interval"
+}
diff --git a/schemas/ubus/fast.line_status.json b/schemas/ubus/fast.line_status.json
new file mode 100644
index 0000000..498ac00
--- /dev/null
+++ b/schemas/ubus/fast.line_status.json
@@ -0,0 +1,122 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "$id": "https://www.iopsys.eu/fast.line_status.json",
+  "title": "JSON Schema for the output of the UBUS call: fast.line.x status",
+
+  "definitions": {
+
+    "if_status": {
+      "type": "string",
+      "enum": [ "up", "down", "dormant", "not_present", "lower_layer_down", "error", "unknown" ]
+    },
+
+    "link_status": {
+      "type": "string",
+      "enum": [ "up", "initializing", "establishing", "no_signal", "disabled", "error", "unknown" ]
+    },
+
+    "fast_profile": {
+      "type": "string",
+      "enum": [ "106a", "212a", "unknown" ]
+    },
+
+    "power_mgmt_state": {
+      "type": "string",
+      "enum": [ "l0", "l2_1", "l2_2", "l3", "unknown" ]
+    },
+
+    "integer_array": {
+      "type": "array",
+      "items": { "type": "integer" },
+      "minItems": 0
+    },
+
+    "bidirectional_integers": {
+      "type": "object",
+      "properties": {
+        "us": { "type": "integer" },
+        "ds": { "type": "integer" }
+      },
+      "required": [ "us", "ds" ]
+    },
+
+    "bidirectional_bools": {
+      "type": "object",
+      "properties": {
+        "us": { "type": "boolean" },
+        "ds": { "type": "boolean" }
+      },
+      "required": [ "us", "ds" ]
+    },
+
+    "line_status": {
+      "$id": "#line_status",
+      "type": "object",
+
+      "properties" : {
+
+        "status": { "$ref": "#/definitions/if_status" },
+        "upstream": { "type": "boolean" },
+        "firmware_version": {"type": "string" },
+        "link_status": { "$ref": "#/definitions/link_status" },
+        "current_profile": { "$ref": "#/definitions/fast_profile" },
+        "power_management_state": { "$ref": "#/definitions/power_mgmt_state" },
+        "max_bit_rate": { "$ref": "#/definitions/bidirectional_integers" },
+
+        "allowed_profiles": {
+          "type": "array",
+          "items": { "$ref": "#/definitions/fast_profile" },
+          "minItems": 0
+        },
+
+        "success_failure_cause": { "type": "integer" },
+        "upbokler": { "type": "integer" },
+        "last_transmitted_signal": { "$ref": "#/definitions/bidirectional_integers" },
+        "upbokle": { "type": "integer" },
+        "line_number": { "type": "integer" },
+        
+        
+        "noise_margin": { "$ref": "#/definitions/bidirectional_integers" },
+        "last_state_transmitted": { "$ref": "#/definitions/bidirectional_integers" },
+        "attenuation": { "type": "integer" },
+        "trellis": { "$ref": "#/definitions/bidirectional_integers" },
+        "power": { "$ref": "#/definitions/bidirectional_integers" },
+        
+        "snrm_rmc": { "$ref": "#/definitions/bidirectional_integers" },
+        "bits_rmc_ps": { "$ref": "#/definitions/integer_array" },
+        "attenuation": { "$ref": "#/definitions/bidirectional_integers" },
+        "fext_to_cancel_enable": { "$ref": "#/definitions/bidirectional_bools" },
+        "etr": { "$ref": "#/definitions/bidirectional_integers" },
+        "att_etr": { "$ref": "#/definitions/bidirectional_integers" },
+        "min_eftr": { "$ref": "#/definitions/bidirectional_integers" }
+      },
+
+      "required": [
+        "status",
+        "upstream",
+        "firmware_version",
+        "link_status",
+        "current_profile",
+        "power_management_state",
+        "max_bit_rate",
+        "allowed_profiles",
+        "success_failure_cause",
+        "upbokler",
+        "last_transmitted_signal",
+        "upbokle",
+        "line_number",
+        "noise_margin",
+        "attenuation",
+        "power",
+        "snrm_rmc",
+        "bits_rmc_ps",
+        "fext_to_cancel_enable",
+        "etr",
+        "att_etr",
+        "min_eftr"
+      ]
+    }
+  },
+
+  "$ref": "#line_status"
+}
diff --git a/schemas/ubus/ptm.link_status.json b/schemas/ubus/ptm.link_status.json
new file mode 100644
index 0000000..9e7474b
--- /dev/null
+++ b/schemas/ubus/ptm.link_status.json
@@ -0,0 +1,37 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "$id": "https://www.iopsys.eu/ptm.link_status.json",
+  "title": "JSON Schema for the output of the UBUS call: ptm.link.x status",
+
+  "definitions": {
+
+    "if_status": {
+      "type": "string",
+      "enum": [ "up", "down", "dormant", "not_present", "lower_layer_down", "error", "unknown" ]
+    },
+
+    "mac_address": {
+      "type": "string",
+      "pattern": "[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}",
+      "minLength": 17,
+      "maxLength": 17
+    },
+
+    "ptm_link_status": {
+      "$id": "#ptm_link_status",
+      "type": "object",
+
+      "properties" : {
+        "status": { "$ref": "#/definitions/if_status" },
+        "mac_addr": { "$ref": "#/definitions/mac_address" }
+      },
+
+      "required": [
+        "status",
+        "mac_addr"
+      ]
+    }
+  },
+
+  "$ref": "#ptm_link_status"
+}
diff --git a/test/api/json/atm.link.validation.json b/test/api/json/atm.link.validation.json
new file mode 100644
index 0000000..519ea68
--- /dev/null
+++ b/test/api/json/atm.link.validation.json
@@ -0,0 +1,30 @@
+{
+  "object": "atm.link.0",
+
+  "methods": [
+    {
+      "method": "status",
+      "rc": 0
+    },
+
+    {
+      "method": "stats",
+      "rc": 0
+    },
+
+    {
+      "method": "configure",
+      "args": {
+        "link_type": "eoa",
+        "vpi": 8,
+        "vci": 35,
+        "encapsulation": "llc",
+        "qos_class": "ubr"
+      },
+      "rc": 0
+    }
+  ]
+}
+
+ubus call atm.link.0 configure "{'link_type':'eoa','vpi':8,'vci':35,'encapsulation':'llc',\
+    'qos_class':'ubr','peak_cell_rate':10240,'max_burst_size':8124,'sustainable_cell_rate':9600}"
diff --git a/test/api/json/dsl.line.validation.json b/test/api/json/dsl.line.validation.json
index f7358f4..b420b88 100644
--- a/test/api/json/dsl.line.validation.json
+++ b/test/api/json/dsl.line.validation.json
@@ -51,5 +51,18 @@
       },
       "rc": 0
     },
+
+    {
+      "method": "configure",
+      "args": {
+        "xtse": "01,02,ab,cd,41,52,f8,6e",
+        "vdsl2_profiles": "8a,8b,8c,8d,17a,30a,35b",
+        "fast_profiles": "106a,212a",
+        "data_gathering": true,
+        "limit_mask": 7,
+        "us0_mask": 3
+      },
+      "rc": 0
+    }
   ]
 }
diff --git a/test/api/json/fast.line.validation.json b/test/api/json/fast.line.validation.json
new file mode 100644
index 0000000..df93e28
--- /dev/null
+++ b/test/api/json/fast.line.validation.json
@@ -0,0 +1,55 @@
+{
+  "object": "fast.line.0",
+
+  "methods": [
+    {
+      "method": "status",
+      "rc": 0
+    },
+
+    {
+      "method": "stats",
+      "rc": 0
+    },
+
+    {
+      "method": "stats",
+      "args": {
+        "interval": "total"
+      },
+      "rc": 0
+    },
+
+    {
+      "method": "stats",
+      "args": {
+        "interval": "showtime"
+      },
+      "rc": 0
+    },
+
+    {
+      "method": "stats",
+      "args": {
+        "interval": "lastshowtime"
+      },
+      "rc": 0
+    },
+
+    {
+      "method": "stats",
+      "args": {
+        "interval": "currentday"
+      },
+      "rc": 0
+    },
+
+    {
+      "method": "stats",
+      "args": {
+        "interval": "quarterhour"
+      },
+      "rc": 0
+    }
+  ]
+}
diff --git a/test/api/json/ptm.link.validation.json b/test/api/json/ptm.link.validation.json
new file mode 100644
index 0000000..e5e973e
--- /dev/null
+++ b/test/api/json/ptm.link.validation.json
@@ -0,0 +1,10 @@
+{
+  "object": "ptm.link.0",
+
+  "methods": [
+    {
+      "method": "status",
+      "rc": 0
+    }
+  ]
+}
diff --git a/test/cmocka/unit_test_dslmngr.c b/test/cmocka/unit_test_dslmngr.c
index 465a80a..4099e4c 100644
--- a/test/cmocka/unit_test_dslmngr.c
+++ b/test/cmocka/unit_test_dslmngr.c
@@ -19,9 +19,15 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  * 02110-1301 USA
  */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
 #include <stdarg.h>
 #include <stddef.h>
 #include <setjmp.h>
+#include <errno.h>
 #include <cmocka.h>
 
 #include <libubus.h>
@@ -36,11 +42,14 @@
 #include "dslmngr.h"
 #include "utils.h"
 
+#define LIBDSL_LOG_INPUT_PARAMS "/tmp/libdsl-input-params.log"
+
 struct test_ctx {
 	struct blob_buf bb;
 	struct ubus_object dsl;
 	struct ubus_object dsl_line;
 	struct ubus_object dsl_channel;
+	struct ubus_object atm_link;
 };
 
 /* Overload ubus_send_reply to prevent segment fault*/
@@ -66,6 +75,9 @@ static int setup(void **state)
 
 	blob_buf_init(&ctx->bb, 0);
 
+	// Delete the log file for libdsl input parameters dump
+	unlink(LIBDSL_LOG_INPUT_PARAMS);
+
 	return 0;
 }
 
@@ -89,6 +101,7 @@ static int group_setup(void **state)
 	ctx->dsl.name = "dsl";
 	ctx->dsl_line.name = "dsl.line.0";
 	ctx->dsl_channel.name = "dsl.channel.0";
+	ctx->atm_link.name = "atm.link.0";
 	memset(&ctx->bb, 0, sizeof(struct blob_buf));
 	*state = ctx;
 
@@ -223,6 +236,210 @@ static void test_api_dsl_stats(void **state)
 	validate_dsl_stats(json_output);
 }
 
+static struct json_object *get_json_obj_from_log()
+{
+	int fd = -1;
+	char *str = NULL;
+
+	// Put the output of ubus call into a string
+	fd = open(LIBDSL_LOG_INPUT_PARAMS, O_RDONLY);
+	assert_return_code(fd, errno);
+
+	struct stat st;
+	int rc = fstat(fd, &st);
+	assert_true(rc == 0 && st.st_size > 0);
+
+	str = calloc(1, (size_t)st.st_size + 1);
+	assert_non_null(str);
+
+	ssize_t read_len = read(fd, str, (size_t)st.st_size);
+	assert_int_equal((int)read_len, (int)st.st_size);
+
+	// Parse the string to a json_object
+	char *start = strchr(str, '{');
+	assert_non_null(start);
+	//puts(start);
+	struct json_object *obj = json_tokener_parse(start);
+
+	if (fd >= 0)
+		close(fd);
+	if (str)
+		free(str);
+
+	return obj;
+}
+
+static void test_api_dsl_configure(void **state)
+{
+	const char *xtse_val = "01,02,ab,cd,41,52,f8,6e";
+	const char *vdsl2_profiles_val = "8a,8b,8c,8d,17a,30a,35b";
+	const char *fast_profiles_val = "106a,212a";
+	const bool data_gathering_val = true;
+	const unsigned int limit_mask_val = 7;
+	const unsigned int us0_mask_val = 3;
+
+	struct test_ctx *ctx = (struct test_ctx *) *state;
+	struct blob_buf *bb = &ctx->bb;
+	struct ubus_object *obj = &ctx->dsl_line;
+
+	// Prepare the input parameters
+	blobmsg_add_string(bb, "xtse", xtse_val);
+	blobmsg_add_string(bb, "vdsl2_profiles", vdsl2_profiles_val);
+	blobmsg_add_string(bb, "fast_profiles", fast_profiles_val);
+	blobmsg_add_u8(bb, "data_gathering", data_gathering_val);
+	blobmsg_add_u32(bb, "limit_mask", limit_mask_val);
+	blobmsg_add_u32(bb, "us0_mask", us0_mask_val);
+
+	// Call the function to be tested
+	int res = dsl_line_configure(NULL, obj, NULL, NULL, bb->head);
+	assert_true(res == UBUS_STATUS_OK);
+
+	// Check the results
+	struct json_object *jobj = get_json_obj_from_log();
+	assert_non_null(jobj);
+
+	struct json_object *tmp = json_object_get_by_string(jobj, "xtse");
+	assert_string_equal(json_object_get_string(tmp), xtse_val);
+
+	tmp = json_object_get_by_string(jobj, "vdsl2_profiles");
+	assert_string_equal(json_object_get_string(tmp), vdsl2_profiles_val);
+
+	tmp = json_object_get_by_string(jobj, "fast_profiles");
+	assert_string_equal(json_object_get_string(tmp), fast_profiles_val);
+
+	tmp = json_object_get_by_string(jobj, "limit_mask");
+	assert_int_equal(json_object_get_int(tmp), limit_mask_val);
+
+	tmp = json_object_get_by_string(jobj, "us0_mask");
+	assert_int_equal(json_object_get_int(tmp), us0_mask_val);
+
+	// Free the memory
+	json_object_put(jobj);
+}
+
+static void test_api_dsl_configure_fail(void **state)
+{
+	const char *xtse_val = "00,00,00,00,00,00,00,00"; // invalid
+	const char *vdsl2_profiles_val = "30a,35b";
+	const char *fast_profiles_val = "106a";
+
+	struct test_ctx *ctx = (struct test_ctx *) *state;
+	struct blob_buf *bb = &ctx->bb;
+	struct ubus_object *obj = &ctx->dsl_line;
+
+	// Prepare the input parameters
+	blobmsg_add_string(bb, "xtse", xtse_val);
+	blobmsg_add_string(bb, "vdsl2_profiles", vdsl2_profiles_val);
+	blobmsg_add_string(bb, "fast_profiles", fast_profiles_val);
+
+	// Call the function to be tested
+	int res = dsl_line_configure(NULL, obj, NULL, NULL, bb->head);
+	assert_true(res != UBUS_STATUS_OK);
+
+	// Check the results
+	struct json_object *jobj = get_json_obj_from_log();
+	assert_non_null(jobj);
+
+	struct json_object *tmp = json_object_get_by_string(jobj, "xtse");
+	assert_string_equal(json_object_get_string(tmp), xtse_val);
+
+	tmp = json_object_get_by_string(jobj, "vdsl2_profiles");
+	assert_string_equal(json_object_get_string(tmp), vdsl2_profiles_val);
+
+	tmp = json_object_get_by_string(jobj, "fast_profiles");
+	assert_string_equal(json_object_get_string(tmp), fast_profiles_val);
+
+	// Free the memory
+	json_object_put(jobj);
+}
+
+static void test_api_atm_configure(void **state)
+{
+	const char *link_type_val = "eoa";
+	const unsigned int vpi_val = 8; // invalid
+	const unsigned int vci_val = 35;
+	const char *qos_class_val = "ubr";
+	const unsigned int max_burst_size_val = 8124;
+
+	struct test_ctx *ctx = (struct test_ctx *) *state;
+	struct blob_buf *bb = &ctx->bb;
+	struct ubus_object *obj = &ctx->atm_link;
+
+	// Prepare the input parameters
+	blobmsg_add_string(bb, "link_type", link_type_val);
+	blobmsg_add_u32(bb, "vpi", vpi_val);
+	blobmsg_add_u32(bb, "vci", vci_val);
+	blobmsg_add_string(bb, "qos_class", qos_class_val);
+	blobmsg_add_u32(bb, "max_burst_size", max_burst_size_val);
+
+	// Call the function to be tested
+	int res = atm_link_configure(NULL, obj, NULL, NULL, bb->head);
+	assert_true(res == UBUS_STATUS_OK);
+
+	// Check the results
+	struct json_object *jobj = get_json_obj_from_log();
+	assert_non_null(jobj);
+
+	struct json_object *tmp = json_object_get_by_string(jobj, "link_type");
+	assert_string_equal(json_object_get_string(tmp), link_type_val);
+
+	tmp = json_object_get_by_string(jobj, "vpi");
+	assert_int_equal(json_object_get_int(tmp), vpi_val);
+
+	tmp = json_object_get_by_string(jobj, "vci");
+	assert_int_equal(json_object_get_int(tmp), vci_val);
+
+	tmp = json_object_get_by_string(jobj, "qos_class");
+	assert_string_equal(json_object_get_string(tmp), qos_class_val);
+
+	tmp = json_object_get_by_string(jobj, "max_burst_size");
+	assert_int_equal(json_object_get_int(tmp), max_burst_size_val);
+
+	// Free the memory
+	json_object_put(jobj);
+}
+
+static void test_api_atm_configure_fail(void **state)
+{
+	const char *link_type_val = "eoa";
+	const unsigned int vpi_val = 0; // invalid
+	const unsigned int vci_val = 35;
+	const char *qos_class_val = "ubr";
+
+	struct test_ctx *ctx = (struct test_ctx *) *state;
+	struct blob_buf *bb = &ctx->bb;
+	struct ubus_object *obj = &ctx->atm_link;
+
+	// Prepare the input parameters
+	blobmsg_add_string(bb, "link_type", link_type_val);
+	blobmsg_add_u32(bb, "vpi", vpi_val);
+	blobmsg_add_u32(bb, "vci", vci_val);
+	blobmsg_add_string(bb, "qos_class", qos_class_val);
+
+	// Call the function to be tested
+	int res = atm_link_configure(NULL, obj, NULL, NULL, bb->head);
+	assert_true(res != UBUS_STATUS_OK);
+
+	// Check the results
+	struct json_object *jobj = get_json_obj_from_log();
+	assert_non_null(jobj);
+
+	struct json_object *tmp = json_object_get_by_string(jobj, "link_type");
+	assert_string_equal(json_object_get_string(tmp), link_type_val);
+
+	tmp = json_object_get_by_string(jobj, "vpi");
+	assert_int_equal(json_object_get_int(tmp), vpi_val);
+
+	tmp = json_object_get_by_string(jobj, "vci");
+	assert_int_equal(json_object_get_int(tmp), vci_val);
+
+	tmp = json_object_get_by_string(jobj, "qos_class");
+	assert_string_equal(json_object_get_string(tmp), qos_class_val);
+
+	// Free the memory
+	json_object_put(jobj);
+}
+
 int main(void)
 {
 	const struct CMUnitTest tests[] = {
@@ -233,7 +450,11 @@ int main(void)
 		cmocka_unit_test_setup_teardown(test_api_dsl_line_stats_interval, setup, teardown),
 		cmocka_unit_test_setup_teardown(test_api_dsl_channel_stats, setup, teardown),
 		cmocka_unit_test_setup_teardown(test_api_dsl_channel_stats_interval, setup, teardown),
-		cmocka_unit_test_setup_teardown(test_api_dsl_stats, setup, teardown)
+		cmocka_unit_test_setup_teardown(test_api_dsl_stats, setup, teardown),
+		cmocka_unit_test_setup_teardown(test_api_dsl_configure, setup, teardown),
+		cmocka_unit_test_setup_teardown(test_api_dsl_configure_fail, setup, teardown),
+		cmocka_unit_test_setup_teardown(test_api_atm_configure, setup, teardown),
+		cmocka_unit_test_setup_teardown(test_api_atm_configure_fail, setup, teardown)
 	};
 
 	return cmocka_run_group_tests(tests, group_setup, group_teardown);
-- 
GitLab