/* * dslmngr.c - provides "dsl" UBUS object * * Copyright (C) 2019 iopsys Software Solutions AB. All rights reserved. * * Author: anjan.chanda@iopsys.eu * yalu.zhang@iopsys.eu * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #ifndef _GNU_SOURCE # define _GNU_SOURCE // For asprintf() #endif #include <stdio.h> #include <libubox/blobmsg.h> #include <libubox/blobmsg_json.h> #include <libubox/uloop.h> #include <libubox/ustream.h> #include <libubox/utils.h> #include <uci.h> #include <net/if.h> #include <easy/easy.h> #include "xdsl.h" #include "xtm.h" #include "dslmngr.h" #define DSL_OBJECT_LINE "line" #define DSL_OBJECT_CHANNEL "channel" struct value2text { int value; char *text; }; int current_log_level = LOG_WARNING; enum { DSL_STATS_INTERVAL, __DSL_STATS_MAX, }; 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) { case IF_UP: return "up"; case IF_DOWN: return "down"; case IF_DORMANT: return "dormant"; case IF_NOTPRESENT: return "not_present"; case IF_LLDOWN: return "lower_layer_down"; case IF_ERROR: return "error"; case IF_UNKNOWN: default: return "unknown"; } } static const char *dsl_link_status_str(enum dsl_link_status status) { switch (status) { case LINK_UP: return "up"; case LINK_INITIALIZING: return "initializing"; case LINK_ESTABLISHING: return "establishing"; case LINK_NOSIGNAL: return "no_signal"; case LINK_DISABLED: return "disabled"; case LINK_ERROR: return "error"; default: return "unknown"; } } static const char *dsl_mod_str(enum dsl_modtype mod) { switch (mod) { case MOD_G_922_1_ANNEX_A: return "gdmt_annexa"; case MOD_G_922_1_ANNEX_B: return "gdmt_annexb"; case MOD_G_922_1_ANNEX_C: return "gdmt_annexc"; case MOD_T1_413: return "t1413"; case MOD_T1_413i2: return "t1413_i2"; case MOD_ETSI_101_388: return "etsi_101_388"; case MOD_G_992_2: return "glite"; case MOD_G_992_3_Annex_A: return "adsl2_annexa"; case MOD_G_992_3_Annex_B: return "adsl2_annexb"; case MOD_G_992_3_Annex_C: return "adsl2_annexc"; case MOD_G_992_3_Annex_I: return "adsl2_annexi"; case MOD_G_992_3_Annex_J: return "adsl2_annexj"; case MOD_G_992_3_Annex_L: return "adsl2_annexl"; case MOD_G_992_3_Annex_M: return "adsl2_annexm"; case MOD_G_992_4: return "splitterless_adsl2"; case MOD_G_992_5_Annex_A: return "adsl2p_annexa"; case MOD_G_992_5_Annex_B: return "adsl2p_annexb"; case MOD_G_992_5_Annex_C: return "adsl2p_annexc"; case MOD_G_992_5_Annex_I: return "adsl2p_annexi"; case MOD_G_992_5_Annex_J: return "adsl2p_annexj"; case MOD_G_992_5_Annex_M: return "adsl2p_annexm"; case MOD_G_993_1: return "vdsl"; case MOD_G_993_1_Annex_A: return "vdsl_annexa"; case MOD_G_993_2_Annex_A: return "vdsl2_annexa"; case MOD_G_993_2_Annex_B: return "vdsl2_annexb"; case MOD_G_993_2_Annex_C: return "vdsl2_annexc"; default: return "unknown"; } } static const char *dsl_xtse_str(enum dsl_xtse_bit xtse) { switch (xtse) { /* Octet 1 - ADSL, i.e. g.dmt */ case T1_413: return dsl_mod_str(MOD_T1_413); case ETSI_101_388: return dsl_mod_str(MOD_ETSI_101_388); case G_992_1_POTS_NON_OVERLAPPED: return dsl_mod_str(MOD_G_922_1_ANNEX_A); case G_992_1_POTS_OVERLAPPED: return dsl_mod_str(MOD_G_922_1_ANNEX_A); case G_992_1_ISDN_NON_OVERLAPPED: return dsl_mod_str(MOD_G_922_1_ANNEX_B); case G_992_1_ISDN_OVERLAPPED: return dsl_mod_str(MOD_G_922_1_ANNEX_B); case G_992_1_TCM_ISDN_NON_OVERLAPPED: return dsl_mod_str(MOD_G_922_1_ANNEX_C); case G_992_1_TCM_ISDN_OVERLAPPED: return dsl_mod_str(MOD_G_922_1_ANNEX_C); /* Octet 2 - Splitter-less ADSL, i.e. g.lite */ case G_992_2_POTS_NON_OVERLAPPED: return dsl_mod_str(MOD_G_992_2); case G_992_2_POTS_OVERLAPPED: return dsl_mod_str(MOD_G_992_2); case G_992_2_TCM_ISDN_NON_OVERLAPPED: return dsl_mod_str(MOD_G_992_2); case G_992_2_TCM_ISDN_OVERLAPPED: return dsl_mod_str(MOD_G_992_2); /* Bits 13 - 16 are reserved */ /* Octet 3 - ADSL2 */ /* Bits 17 - 18 are reserved */ case G_992_3_POTS_NON_OVERLAPPED: return dsl_mod_str(MOD_G_992_3_Annex_A); case G_992_3_POTS_OVERLAPPED: return dsl_mod_str(MOD_G_992_3_Annex_A); case G_992_3_ISDN_NON_OVERLAPPED: return dsl_mod_str(MOD_G_992_3_Annex_B); case G_992_3_ISDN_OVERLAPPED: return dsl_mod_str(MOD_G_992_3_Annex_B); case G_992_3_TCM_ISDN_NON_OVERLAPPED: return dsl_mod_str(MOD_G_992_3_Annex_C); case G_992_3_TCM_ISDN_OVERLAPPED: return dsl_mod_str(MOD_G_992_3_Annex_C); /* Octet 4 - Splitter-less ADSL2 and ADSL2 */ case G_992_4_POTS_NON_OVERLAPPED: return dsl_mod_str(MOD_G_992_4); case G_992_4_POTS_OVERLAPPED: return dsl_mod_str(MOD_G_992_4); /* Bits 27 - 28 are reserved */ case G_992_3_ANNEX_I_NON_OVERLAPPED: return dsl_mod_str(MOD_G_992_3_Annex_I); case G_992_3_ANNEX_I_OVERLAPPED: return dsl_mod_str(MOD_G_992_3_Annex_I); case G_992_3_ANNEX_J_NON_OVERLAPPED: return dsl_mod_str(MOD_G_992_3_Annex_J); case G_992_3_ANNEX_J_OVERLAPPED: return dsl_mod_str(MOD_G_992_3_Annex_J); /* Octet 5 - Splitter-less ADSL2 and ADSL2 */ case G_992_4_ANNEX_I_NON_OVERLAPPED: return dsl_mod_str(MOD_G_992_4); case G_992_4_ANNEX_I_OVERLAPPED: return dsl_mod_str(MOD_G_992_4); case G_992_3_POTS_MODE_1: return dsl_mod_str(MOD_G_992_3_Annex_L); case G_992_3_POTS_MODE_2: return dsl_mod_str(MOD_G_992_3_Annex_L); case G_992_3_POTS_MODE_3: return dsl_mod_str(MOD_G_992_3_Annex_L); case G_992_3_POTS_MODE_4: return dsl_mod_str(MOD_G_992_3_Annex_L); case G_992_3_EXT_POTS_NON_OVERLAPPED: return dsl_mod_str(MOD_G_992_3_Annex_M); case G_992_3_EXT_POTS_OVERLAPPED: return dsl_mod_str(MOD_G_992_3_Annex_M); /* Octet 6 - ADSL2+ */ case G_992_5_POTS_NON_OVERLAPPED: return dsl_mod_str(MOD_G_992_5_Annex_A); case G_992_5_POTS_OVERLAPPED: return dsl_mod_str(MOD_G_992_5_Annex_A); case G_992_5_ISDN_NON_OVERLAPPED: return dsl_mod_str(MOD_G_992_5_Annex_B); case G_992_5_ISDN_OVERLAPPED: return dsl_mod_str(MOD_G_992_5_Annex_B); case G_992_5_TCM_ISDN_NON_OVERLAPPED: return dsl_mod_str(MOD_G_992_5_Annex_C); case G_992_5_TCM_ISDN_OVERLAPPED: return dsl_mod_str(MOD_G_992_5_Annex_C); case G_992_5_ANNEX_I_NON_OVERLAPPED: return dsl_mod_str(MOD_G_992_5_Annex_I); case G_992_5_ANNEX_I_OVERLAPPED: return dsl_mod_str(MOD_G_992_5_Annex_I); /* Octet 7 - ADSL2+ */ case G_992_5_ANNEX_J_NON_OVERLAPPED: return dsl_mod_str(MOD_G_992_5_Annex_J); case G_992_5_ANNEX_J_OVERLAPPED: return dsl_mod_str(MOD_G_992_5_Annex_J); case G_992_5_EXT_POTS_NON_OVERLAPPED: return dsl_mod_str(MOD_G_992_5_Annex_M); case G_992_5_EXT_POTS_OVERLAPPED: return dsl_mod_str(MOD_G_992_5_Annex_M); /* Bits 53 - 56 are reserved */ /* Octet 8 - VDSL2 */ case G_993_2_NORTH_AMERICA: return dsl_mod_str(MOD_G_993_2_Annex_A); case G_993_2_EUROPE: return dsl_mod_str(MOD_G_993_2_Annex_B); case G_993_2_JAPAN: return dsl_mod_str(MOD_G_993_2_Annex_C); /* Bits 60 - 64 are reserved */ default: return "unknown"; } } static const char *dsl_line_encoding_str(enum dsl_line_encoding encoding) { switch (encoding) { case LE_DMT: return "dmt"; case LE_CAP: return "cap"; case LE_2B1Q: return "2b1q"; case LE_43BT: return "43bt"; case LE_PAM: return "pam"; case LE_QAM: return "qam"; default: return "unknown"; } } static const char *dsl_profile_str(enum dsl_profile profile) { 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) { switch (power_state) { case DSL_L0: return "l0"; case DSL_L1: return "l1"; case DSL_L2: return "l2"; case DSL_L3: return "l3"; case DSL_L4: return "l4"; default: return "unknown"; } }; static void dsl_add_sequence_to_blob(const char *name, bool is_signed, size_t count, const long *head, struct blob_buf *bb) { void *array; size_t i; array = blobmsg_open_array(bb, name); for (i = 0; i < count; i++) { if (is_signed) blobmsg_add_u32(bb, "", (uint32_t)head[i]); else blobmsg_add_u64(bb, "", (uint32_t)head[i]); } blobmsg_close_array(bb, array); } static void dsl_add_usds_to_blob(const char *name, bool is_signed, const long *head, struct blob_buf *bb) { void *table; int i; table = blobmsg_open_table(bb, name); for (i = 0; i < 2; i++) { if (is_signed) blobmsg_add_u32(bb, i == 0 ? "us" : "ds", (uint32_t)head[i]); else blobmsg_add_u64(bb, i == 0 ? "us" : "ds", (uint32_t)head[i]); } blobmsg_close_table(bb, table); } static void dsl_add_int_to_blob(const char *name, long int value, struct blob_buf *bb) { blobmsg_add_u32(bb, name, (uint32_t)value); } static void dsl_status_line_to_blob(const struct dsl_line *line, struct blob_buf *bb) { void *array; int i, j, count; unsigned long opt; char str[64], *modes[ARRAY_SIZE(line->standard_used.xtse) * 8] = { NULL, }; 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)); // standard_used if (line->standard_used.use_xtse) { array = blobmsg_open_array(bb, "xtse_used"); for (i = 0; i < ARRAY_SIZE(line->standard_used.xtse); i++) { snprintf(str, sizeof(str), "%02x", line->standard_used.xtse[i]); blobmsg_add_string(bb, "", str); } blobmsg_close_array(bb, array); // For backward compatibility, provide the old format as well for (i = T1_413; i <= G_993_2_JAPAN; i++) { if (XTSE_BIT_GET(line->standard_used.xtse, i)) { blobmsg_add_string(bb, "standard_used", dsl_xtse_str(i)); break; } } } else { for (opt = (unsigned long)MOD_G_922_1_ANNEX_A; opt <= (unsigned long)MOD_G_993_2_Annex_C; opt <<= 1) { if (line->standard_used.mode & opt) { blobmsg_add_string(bb, "standard_used", dsl_mod_str(opt)); break; } } } // current_profile blobmsg_add_string(bb, "current_profile", dsl_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); blobmsg_add_string(bb, "line_encoding", dsl_line_encoding_str(line->line_encoding)); // standards_supported if (line->standard_supported.use_xtse) { array = blobmsg_open_array(bb, "xtse"); for (i = 0; i < ARRAY_SIZE(line->standard_supported.xtse); i++) { snprintf(str, sizeof(str), "%02x", line->standard_supported.xtse[i]); blobmsg_add_string(bb, "", str); } blobmsg_close_array(bb, array); // For backward compatibility, provide the old format as well array = blobmsg_open_array(bb, "standards_supported"); for (i = T1_413, count = 0; i <= G_993_2_JAPAN; i++) { if (XTSE_BIT_GET(line->standard_supported.xtse, i)) { /* More than one XTSE bits can be mapped to the same old standard. So we need to filter * out the repeated ones. */ const char *mod = dsl_xtse_str(i); for (j = 0; j < count; j++) { if (strcmp(mod, modes[j]) == 0) break; } if (j == count) { // Not found modes[count++] = (char *)mod; blobmsg_add_string(bb, "", mod); } } } blobmsg_close_array(bb, array); } else { array = blobmsg_open_array(bb, "standards_supported"); for (opt = (unsigned long)MOD_G_922_1_ANNEX_A; opt <= (unsigned long)MOD_G_993_2_Annex_C; opt <<= 1) { if (line->standard_supported.mode & opt) blobmsg_add_string(bb, "", dsl_mod_str(opt)); } blobmsg_close_array(bb, array); } // allowed_profiles array = blobmsg_open_array(bb, "allowed_profiles"); for (opt = (unsigned long)VDSL2_8a; opt <= (unsigned long)VDSL2_35b; opt <<= 1) { if (line->allowed_profiles & opt) blobmsg_add_string(bb, "", dsl_profile_str(opt)); } blobmsg_close_array(bb, array); blobmsg_add_u32(bb, "success_failure_cause", line->success_failure_cause); // upbokler_pb dsl_add_sequence_to_blob("upbokler_pb", false, line->upbokler_pb.count, (const long *)line->upbokler_pb.array, bb); // rxthrsh_ds dsl_add_sequence_to_blob("rxthrsh_ds", false, line->rxthrsh_ds.count, (const long *)line->rxthrsh_ds.array, bb); // act_ra_mode unsigned long ra_modes[] = { line->act_ra_mode.us, line->act_ra_mode.ds }; dsl_add_usds_to_blob("act_ra_mode", false, ra_modes, bb); blobmsg_add_u64(bb, "snr_mroc_us", line->snr_mroc.us); // last_state_transmitted unsigned long lst[] = { line->last_state_transmitted.us, line->last_state_transmitted.ds }; dsl_add_usds_to_blob("last_state_transmitted", false, lst, bb); // us0_mask blobmsg_add_u64(bb, "us0_mask", line->us0_mask); // trellis long trellis[] = { line->trellis.us, line->trellis.ds }; dsl_add_usds_to_blob("trellis", true, trellis, bb); // act_snr_mode unsigned long snr_modes[] = { line->act_snr_mode.us, line->act_snr_mode.ds }; dsl_add_usds_to_blob("act_snr_mode", false, snr_modes, bb); // line_number 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); // snr_mpb_us dsl_add_sequence_to_blob("snr_mpb_us", true, line->snr_mpb_us.count, (const long *)line->snr_mpb_us.array, bb); // snr_mpb_ds dsl_add_sequence_to_blob("snr_mpb_ds", true, line->snr_mpb_ds.count, (const long *)line->snr_mpb_ds.array, 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); blobmsg_add_string(bb, "xtur_vendor", line->xtur_vendor); blobmsg_add_string(bb, "xtur_country", line->xtur_country); blobmsg_add_u64(bb, "xtur_ansi_std", line->xtur_ansi_std); blobmsg_add_u64(bb, "xtur_ansi_rev", line->xtur_ansi_rev); blobmsg_add_string(bb, "xtuc_vendor", line->xtuc_vendor); blobmsg_add_string(bb, "xtuc_country", line->xtuc_country); blobmsg_add_u64(bb, "xtuc_ansi_std", line->xtuc_ansi_std); blobmsg_add_u64(bb, "xtuc_ansi_rev", line->xtuc_ansi_rev); } static const char *dsl_link_encap_str(enum dsl_link_encapsulation encap) { switch (encap) { case G_992_3_ANNEK_K_ATM: return "adsl2_atm"; case G_992_3_ANNEK_K_PTM: return "adsl2_ptm"; case G_993_2_ANNEK_K_ATM: return "vdsl2_atm"; case G_993_2_ANNEK_K_PTM: return "vdsl2_ptm"; case G_994_1_AUTO: return "auto"; default: return "unknown"; } }; static void dsl_status_channel_to_blob(const struct dsl_channel *channel, struct blob_buf *bb) { void *array; unsigned long opt; blobmsg_add_string(bb, "status", dsl_if_status_str(channel->status)); blobmsg_add_string(bb, "link_encapsulation_used", dsl_link_encap_str(channel->link_encapsulation_used)); // curr_rate unsigned long curr_rates[] = { channel->curr_rate.us, channel->curr_rate.ds }; dsl_add_usds_to_blob("curr_rate", false, curr_rates, bb); // actndr unsigned long act_rates[] = { channel->actndr.us, channel->actndr.ds }; dsl_add_usds_to_blob("actndr", false, act_rates, bb); // link_encapsulation_supported array = blobmsg_open_array(bb, "link_encapsulation_supported"); for (opt = (unsigned long)G_992_3_ANNEK_K_ATM; opt <= (unsigned long)G_994_1_AUTO; opt <<= 1) { if (channel->link_encapsulation_supported & opt) blobmsg_add_string(bb, "", dsl_link_encap_str(opt)); } blobmsg_close_array(bb, array); blobmsg_add_u64(bb, "lpath", channel->lpath); blobmsg_add_u64(bb, "intlvdepth", channel->intlvdepth); dsl_add_int_to_blob("intlvblock", channel->intlvblock, bb); blobmsg_add_u64(bb, "actual_interleaving_delay", channel->actual_interleaving_delay); dsl_add_int_to_blob("actinp", channel->actinp, bb); blobmsg_add_u8(bb, "inpreport", channel->inpreport); dsl_add_int_to_blob("nfec", channel->nfec, bb); dsl_add_int_to_blob("rfec", channel->rfec, bb); dsl_add_int_to_blob("lsymb", channel->lsymb, bb); // actinprein unsigned long act_inps[] = { channel->actinprein.us, channel->actinprein.ds }; dsl_add_usds_to_blob("actinprein", false, act_inps, bb); } int dsl_status_all(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 dsl_line line; struct dsl_channel channel; int retval = UBUS_STATUS_OK; int i, max_line; void *array_line, *array_chan, *table_line, *table_chan; // Initialize the buffer memset(&bb, 0, sizeof(bb)); blob_buf_init(&bb, 0); array_line = blobmsg_open_array(&bb, DSL_OBJECT_LINE); for (i = 0, max_line = dsl_get_line_number(); i < max_line; i++) { if (xdsl_ops.get_line_info == NULL || (*xdsl_ops.get_line_info)(i, &line) != 0 || xdsl_ops.get_channel_info == NULL || (*xdsl_ops.get_channel_info)(i, &channel) != 0) { retval = UBUS_STATUS_UNKNOWN_ERROR; goto __ret; } // Line table table_line = blobmsg_open_table(&bb, ""); // Line parameters blobmsg_add_u32(&bb, "id", (unsigned int)i + 1); dsl_status_line_to_blob(&line, &bb); // Embed channel(s) inside a line in the format channel: [{},{}...] array_chan = blobmsg_open_array(&bb, DSL_OBJECT_CHANNEL); table_chan = blobmsg_open_table(&bb, ""); // Channel parameters blobmsg_add_u32(&bb, "id", 1); dsl_status_channel_to_blob(&channel, &bb); blobmsg_close_table(&bb, table_chan); blobmsg_close_array(&bb, array_chan); blobmsg_close_table(&bb, table_line); } blobmsg_close_array(&bb, array_line); // Send the reply ubus_send_reply(ctx, req, bb.head); __ret: blob_buf_free(&bb); return retval; } static struct value2text dsl_stats_types[] = { { DSL_STATS_TOTAL, "total" }, { DSL_STATS_SHOWTIME, "showtime" }, { DSL_STATS_LASTSHOWTIME, "lastshowtime" }, { DSL_STATS_CURRENTDAY, "currentday" }, { DSL_STATS_QUARTERHOUR, "quarterhour" } }; static void dsl_if_stats_to_blob(const struct if_stats *stats, struct blob_buf *bb) { blobmsg_add_u64(bb, "bytes_sent", stats->tx_bytes); blobmsg_add_u64(bb, "bytes_received", stats->rx_bytes); blobmsg_add_u64(bb, "packets_sent", stats->tx_packets); blobmsg_add_u64(bb, "packets_received", stats->rx_packets); blobmsg_add_u64(bb, "errors_sent", stats->tx_errors); blobmsg_add_u64(bb, "errors_received", stats->rx_errors); blobmsg_add_u64(bb, "discard_packets_sent", stats->tx_dropped); blobmsg_add_u64(bb, "discard_packets_received", stats->rx_dropped); } static void dsl_stats_to_blob(const struct dsl_line_channel_stats *stats, struct blob_buf *bb) { blobmsg_add_u64(bb, "total_start", stats->total_start); blobmsg_add_u64(bb, "showtime_start", stats->showtime_start); blobmsg_add_u64(bb, "last_showtime_start", stats->last_showtime_start); blobmsg_add_u64(bb, "current_day_start", stats->current_day_start); blobmsg_add_u64(bb, "quarter_hour_start", stats->quarter_hour_start); } static void dsl_stats_line_interval_to_blob(const struct dsl_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); } static void dsl_stats_channel_interval_to_blob(const struct dsl_channel_stats_interval *stats, struct blob_buf *bb) { blobmsg_add_u64(bb, "xtur_fec_errors", stats->xtur_fec_errors); blobmsg_add_u64(bb, "xtuc_fec_errors", stats->xtuc_fec_errors); blobmsg_add_u64(bb, "xtur_hec_errors", stats->xtur_hec_errors); blobmsg_add_u64(bb, "xtuc_hec_errors", stats->xtuc_hec_errors); blobmsg_add_u64(bb, "xtur_crc_errors", stats->xtur_crc_errors); blobmsg_add_u64(bb, "xtuc_crc_errors", stats->xtuc_crc_errors); } static int uci_get_ifname(char *ifname, size_t len, bool is_fast); 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 retval = UBUS_STATUS_OK; static struct blob_buf bb; enum dsl_stats_type type; char ifname[IFNAMSIZ]; struct if_stats if_stats = {0}; struct dsl_line_channel_stats stats; struct dsl_line_stats_interval line_stats_interval; struct dsl_channel_stats_interval channel_stats_interval; int i, j, max_line; void *array_line, *array_chan, *table_line, *table_interval, *table_chan; // Initialize the buffer memset(&bb, 0, sizeof(bb)); blob_buf_init(&bb, 0); array_line = blobmsg_open_array(&bb, DSL_OBJECT_LINE); for (i = 0, max_line = dsl_get_line_number(); i < max_line; i++) { if (xdsl_ops.get_line_stats == NULL || (*xdsl_ops.get_line_stats)(i, &stats) != 0) { retval = UBUS_STATUS_UNKNOWN_ERROR; goto __ret; } // Line table table_line = blobmsg_open_table(&bb, ""); // Line statistics blobmsg_add_u32(&bb, "id", (unsigned int)i + 1); if (uci_get_ifname(ifname, sizeof(ifname), false) == 0) if_getstats(ifname, &if_stats); dsl_if_stats_to_blob(&if_stats, &bb); dsl_stats_to_blob(&stats, &bb); // Line interval statistics for (j = 0; j < ARRAY_SIZE(dsl_stats_types); j++) { table_interval = blobmsg_open_table(&bb, dsl_stats_types[j].text); if (xdsl_ops.get_line_stats_interval == NULL || (*xdsl_ops.get_line_stats_interval) (i, dsl_stats_types[j].value, &line_stats_interval) != 0) { retval = UBUS_STATUS_UNKNOWN_ERROR; goto __ret; } dsl_stats_line_interval_to_blob(&line_stats_interval, &bb); blobmsg_close_table(&bb, table_interval); } // Embed channel(s) inside a line in the format channel: [{},{}...] array_chan = blobmsg_open_array(&bb, DSL_OBJECT_CHANNEL); table_chan = blobmsg_open_table(&bb, ""); // Channel statistics blobmsg_add_u32(&bb, "id", 1); if (xdsl_ops.get_channel_stats == NULL || (*xdsl_ops.get_channel_stats)(i, &stats) != 0) { retval = UBUS_STATUS_UNKNOWN_ERROR; goto __ret; } if (uci_get_ifname(ifname, sizeof(ifname), false) == 0) if_getstats(ifname, &if_stats); dsl_if_stats_to_blob(&if_stats, &bb); dsl_stats_to_blob(&stats, &bb); // Line interval statistics for (j = 0; j < ARRAY_SIZE(dsl_stats_types); j++) { table_interval = blobmsg_open_table(&bb, dsl_stats_types[j].text); if (xdsl_ops.get_channel_stats_interval == NULL || (*xdsl_ops.get_channel_stats_interval) (i, dsl_stats_types[j].value, &channel_stats_interval) != 0) { retval = UBUS_STATUS_UNKNOWN_ERROR; goto __ret; } dsl_stats_channel_interval_to_blob(&channel_stats_interval, &bb); blobmsg_close_table(&bb, table_interval); } // Close the tables and arrays for the channel blobmsg_close_table(&bb, table_chan); blobmsg_close_array(&bb, array_chan); // Close the table for one line blobmsg_close_table(&bb, table_line); } blobmsg_close_array(&bb, array_line); // Send the reply ubus_send_reply(ctx, req, bb.head); __ret: blob_buf_free(&bb); return retval; } static struct ubus_method dsl_methods[] = { { .name = "status", .handler = dsl_status_all }, { .name = "stats", .handler = dsl_stats_all } }; static struct ubus_object_type dsl_type = UBUS_OBJECT_TYPE("dsl", dsl_methods); static struct ubus_object dsl_object = { .name = "dsl", .type = &dsl_type, .methods = dsl_methods, .n_methods = ARRAY_SIZE(dsl_methods), }; int dsl_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 dsl_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, "dsl.line.%d", &num); num--; // indexes start from 1 in ubus object if (xdsl_ops.get_line_info == NULL || (*xdsl_ops.get_line_info)(num, &line) != 0) { retval = UBUS_STATUS_UNKNOWN_ERROR; goto __ret; } dsl_status_line_to_blob(&line, &bb); // Send the reply ubus_send_reply(ctx, req, bb.head); __ret: blob_buf_free(&bb); return retval; } static int validate_interval_type(struct blob_attr *msg, enum dsl_stats_type *type) { struct blob_attr *tb[__DSL_STATS_MAX]; int i; 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; } } return UBUS_STATUS_OK; } int dsl_line_stats(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; enum dsl_stats_type type = DSL_STATS_QUARTERHOUR + 1; char ifname[IFNAMSIZ]; struct if_stats if_stats = {0}; struct dsl_line_channel_stats stats; struct dsl_line_stats_interval line_stats_interval; int retval = UBUS_STATUS_OK; int num = -1; int i; void *table; // Parse and validation check the interval type if any if ((retval = validate_interval_type(msg, &type)) != UBUS_STATUS_OK) { return retval; } // Initialize the buffer memset(&bb, 0, sizeof(bb)); blob_buf_init(&bb, 0); // Get line number sscanf(obj->name, "dsl.line.%d", &num); num--; // indexes start from 1 in ubus object // Get line interval statistics if (type >= DSL_STATS_TOTAL && type <= DSL_STATS_QUARTERHOUR) { if (xdsl_ops.get_line_stats_interval == NULL || (*xdsl_ops.get_line_stats_interval) (num, type, &line_stats_interval) != 0) { retval = UBUS_STATUS_UNKNOWN_ERROR; goto __ret; } dsl_stats_line_interval_to_blob(&line_stats_interval, &bb); } else { // Get interface counters if (uci_get_ifname(ifname, sizeof(ifname), false) == 0) if_getstats(ifname, &if_stats); dsl_if_stats_to_blob(&if_stats, &bb); // Get line statistics if (xdsl_ops.get_line_stats == NULL || (*xdsl_ops.get_line_stats)(num, &stats) != 0) { retval = UBUS_STATUS_UNKNOWN_ERROR; goto __ret; } dsl_stats_to_blob(&stats, &bb); // Get all interval statistics for (i = 0; i < ARRAY_SIZE(dsl_stats_types); i++) { table = blobmsg_open_table(&bb, dsl_stats_types[i].text); if (xdsl_ops.get_line_stats_interval == NULL || (*xdsl_ops.get_line_stats_interval) (num, dsl_stats_types[i].value, &line_stats_interval) != 0) { retval = UBUS_STATUS_UNKNOWN_ERROR; goto __ret; } dsl_stats_line_interval_to_blob(&line_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 dsl_line_methods[] = { { .name = "status", .handler = dsl_line_status }, 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); int dsl_channel_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 dsl_channel channel; int retval = UBUS_STATUS_OK; int num = -1; // Initialize the buffer memset(&bb, 0, sizeof(bb)); blob_buf_init(&bb, 0); // Get channel status sscanf(obj->name, "dsl.channel.%d", &num); num--; // indexes start from 1 in ubus object if (xdsl_ops.get_channel_info == NULL || (*xdsl_ops.get_channel_info)(num, &channel) != 0) { retval = UBUS_STATUS_UNKNOWN_ERROR; goto __ret; } dsl_status_channel_to_blob(&channel, &bb); // Send the reply ubus_send_reply(ctx, req, bb.head); __ret: blob_buf_free(&bb); return retval; } int dsl_channel_stats(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; enum dsl_stats_type type = DSL_STATS_QUARTERHOUR + 1; char ifname[IFNAMSIZ]; struct if_stats if_stats = {0}; struct dsl_line_channel_stats stats; struct dsl_channel_stats_interval channel_stats_interval; int retval = UBUS_STATUS_OK; int num = -1; int i; void *table; // Parse and validation check the interval type if any if ((retval = validate_interval_type(msg, &type)) != UBUS_STATUS_OK) { return retval; } // Initialize the buffer memset(&bb, 0, sizeof(bb)); blob_buf_init(&bb, 0); // Get channel number sscanf(obj->name, "dsl.channel.%d", &num); num--; // indexes start from 1 in ubus object // Get channel interval statistics if (type >= DSL_STATS_TOTAL && type <= DSL_STATS_QUARTERHOUR) { if (xdsl_ops.get_channel_stats_interval == NULL || (*xdsl_ops.get_channel_stats_interval) (num, type, &channel_stats_interval) != 0) { retval = UBUS_STATUS_UNKNOWN_ERROR; goto __ret; } dsl_stats_channel_interval_to_blob(&channel_stats_interval, &bb); } else { // Get interface counters if (uci_get_ifname(ifname, sizeof(ifname), false) == 0) if_getstats(ifname, &if_stats); dsl_if_stats_to_blob(&if_stats, &bb); // Get channel statistics if (xdsl_ops.get_channel_stats == NULL || (*xdsl_ops.get_channel_stats)(num, &stats) != 0) { retval = UBUS_STATUS_UNKNOWN_ERROR; goto __ret; } dsl_stats_to_blob(&stats, &bb); // Get all interval statistics for (i = 0; i < ARRAY_SIZE(dsl_stats_types); i++) { table = blobmsg_open_table(&bb, dsl_stats_types[i].text); if (xdsl_ops.get_channel_stats_interval == NULL || (*xdsl_ops.get_channel_stats_interval) (num, dsl_stats_types[i].value, &channel_stats_interval) != 0) { retval = UBUS_STATUS_UNKNOWN_ERROR; goto __ret; } dsl_stats_channel_interval_to_blob(&channel_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 dsl_channel_methods[] = { { .name = "status", .handler = dsl_channel_status }, UBUS_METHOD("stats", dsl_channel_stats, dsl_stats_policy ) }; 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_212c; 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_ds", false, line->bits_rmc_ps_ds.count, (const long *)line->bits_rmc_ps_ds.elements, bb); dsl_add_sequence_to_blob("bits_rmc_ps_us", false, line->bits_rmc_ps_us.count, (const long *)line->bits_rmc_ps_us.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_status_all(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 i, max_line; void *array_line, *table_line; // Initialize the buffer memset(&bb, 0, sizeof(bb)); blob_buf_init(&bb, 0); array_line = blobmsg_open_array(&bb, DSL_OBJECT_LINE); for (i = 0, max_line = dsl_get_line_number(); i < max_line; i++) { if (xdsl_ops.get_fast_line_info == NULL || (*xdsl_ops.get_fast_line_info)(i, &line) != 0) { retval = UBUS_STATUS_UNKNOWN_ERROR; goto __ret; } // Line table table_line = blobmsg_open_table(&bb, ""); // Line parameters blobmsg_add_u32(&bb, "id", (unsigned int)i + 1); fast_line_status_to_blob(&line, &bb); blobmsg_close_table(&bb, table_line); } blobmsg_close_array(&bb, array_line); // 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_stats_all(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) { int retval = UBUS_STATUS_OK; static struct blob_buf bb; enum dsl_stats_type type; struct if_stats if_stats = {0}; struct fast_line_stats stats; struct fast_line_stats_interval line_stats_interval; int i, j, max_line; void *array_line, *table_line, *table_interval; char ifname[IFNAMSIZ]; // Initialize the buffer memset(&bb, 0, sizeof(bb)); blob_buf_init(&bb, 0); array_line = blobmsg_open_array(&bb, DSL_OBJECT_LINE); for (i = 0, max_line = dsl_get_line_number(); i < max_line; i++) { if (xdsl_ops.get_fast_line_stats == NULL || (*xdsl_ops.get_fast_line_stats)(i, &stats) != 0) { retval = UBUS_STATUS_UNKNOWN_ERROR; goto __ret; } // Line table table_line = blobmsg_open_table(&bb, ""); // Line statistics blobmsg_add_u32(&bb, "id", (unsigned int)i + 1); if (uci_get_ifname(ifname, sizeof(ifname), true) == 0) if_getstats(ifname, &if_stats); dsl_if_stats_to_blob(&if_stats, &bb); dsl_stats_to_blob(&stats, &bb); // Line interval statistics for (j = 0; j < ARRAY_SIZE(dsl_stats_types); j++) { table_interval = 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) (i, dsl_stats_types[j].value, &line_stats_interval) != 0) { retval = UBUS_STATUS_UNKNOWN_ERROR; goto __ret; } fast_line_stats_interval_to_blob(&line_stats_interval, &bb); blobmsg_close_table(&bb, table_interval); } // Close the table for one line blobmsg_close_table(&bb, table_line); } blobmsg_close_array(&bb, array_line); // Send the reply ubus_send_reply(ctx, req, bb.head); __ret: blob_buf_free(&bb); return retval; } static struct ubus_method fast_methods[] = { { .name = "status", .handler = fast_status_all }, { .name = "stats", .handler = fast_stats_all } }; static struct ubus_object_type fast_type = UBUS_OBJECT_TYPE("fast", fast_methods); static struct ubus_object fast_object = { .name = "fast", .type = &fast_type, .methods = fast_methods, .n_methods = ARRAY_SIZE(fast_methods), }; 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); num--; // indexes start from 1 in ubus object 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; } 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; enum dsl_stats_type type = DSL_STATS_QUARTERHOUR + 1; struct if_stats if_stats = {0}; struct fast_line_stats stats; struct fast_line_stats_interval stats_interval; int retval = UBUS_STATUS_OK; int num = -1; int i; void *table; char ifname[IFNAMSIZ]; // Parse and validation check the interval type if any if ((retval = validate_interval_type(msg, &type)) != UBUS_STATUS_OK) { return retval; } // Initialize the buffer memset(&bb, 0, sizeof(bb)); blob_buf_init(&bb, 0); // Get line number sscanf(obj->name, "fast.line.%d", &num); num--; // indexes start from 1 in ubus object // 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 interface counters if (uci_get_ifname(ifname, sizeof(ifname), false) == 0) if_getstats(ifname, &if_stats); dsl_if_stats_to_blob(&if_stats, &bb); // 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 (i = 0; i < ARRAY_SIZE(dsl_stats_types); i++) { table = blobmsg_open_table(&bb, dsl_stats_types[i].text); if (xdsl_ops.get_fast_line_stats_interval == NULL || (*xdsl_ops.get_fast_line_stats_interval) (num, dsl_stats_types[i].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); num--; // indexes start from 1 in ubus object 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); num--; // indexes start from 1 in ubus object 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); link_num--; // indexes start from 1 in ubus object 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); num--; // indexes start from 1 in ubus object 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); strncpy(dup, profiles_str, strlen(profiles_str) + 1); 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); strncpy(dup, profiles_str, strlen(profiles_str) + 1); 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); line_num--; // indexes start from 1 in ubus object 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; // 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_object.name, ubus_strerror(ret)); return -1; } // Add objects dsl.line.x max_line = dsl_get_line_number(); line_objects = calloc(max_line, sizeof(struct ubus_object)); if (!line_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, "dsl.line.%d", i + 1) <= 0) { DSLMNGR_LOG(LOG_ERR, "Out of memory\n"); return -1; } line_objects[i].name = obj_name; line_objects[i].type = &dsl_line_type; line_objects[i].methods = dsl_line_methods; line_objects[i].n_methods = ARRAY_SIZE(dsl_line_methods); ret = ubus_add_object(ctx, &line_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 dsl.channel.x max_channel = dsl_get_channel_number(); channel_objects = calloc(max_channel, sizeof(struct ubus_object)); if (!channel_objects) { DSLMNGR_LOG(LOG_ERR, "Out of memory\n"); return -1; } for (i = 0; i < max_channel; i++) { char *obj_name; if (asprintf(&obj_name, "dsl.channel.%d", i + 1) <= 0) { DSLMNGR_LOG(LOG_ERR, "Out of memory\n"); return -1; } channel_objects[i].name = obj_name; channel_objects[i].type = &dsl_channel_type; channel_objects[i].methods = dsl_channel_methods; channel_objects[i].n_methods = ARRAY_SIZE(dsl_channel_methods); ret = ubus_add_object(ctx, &channel_objects[i]); if (ret) { DSLMNGR_LOG(LOG_ERR, "Failed to add UBUS object '%s', %s\n", obj_name, ubus_strerror(ret)); return -1; } } // Add object fast if supported if (xdsl_ops.get_fast_line_info && xdsl_ops.get_fast_line_stats && xdsl_ops.get_fast_line_stats_interval) { ret = ubus_add_object(ctx, &fast_object); if (ret) { DSLMNGR_LOG(LOG_ERR, "Failed to add UBUS object '%s', %s\n", fast_object.name, ubus_strerror(ret)); return -1; } } // 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 + 1) <= 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 + 1) <= 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 + 1) <= 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; __error_ret: if (line_objects) 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; } static int uci_get_ifname(char *ifname, size_t len, bool is_fast) { struct uci_context *ctx; struct uci_package *pkg; struct uci_element *e; const char *value; bool use_ptm = is_fast; int i = 0; // always check line 0 for now /* First, we need to check if we are running ADSL or not */ if (!is_fast) { struct dsl_line line; if (xdsl_ops.get_line_info == NULL || (*xdsl_ops.get_line_info)(i, &line) != 0) { return -1; } if (line.standard_used.use_xtse) { if (XTSE_BIT_GET(line.standard_used.xtse, G_993_2_EUROPE) || XTSE_BIT_GET(line.standard_used.xtse, G_993_2_JAPAN) || XTSE_BIT_GET(line.standard_used.xtse, G_993_2_NORTH_AMERICA)) use_ptm= true; } else { if (line.standard_used.mode & (MOD_G_993_1 | MOD_G_993_1_Annex_A | MOD_G_993_2_Annex_A | MOD_G_993_2_Annex_B | MOD_G_993_2_Annex_C)) use_ptm= true; } } ctx = uci_alloc_context(); if (!ctx) return -1; if (uci_load(ctx, "dsl", &pkg)) { uci_free_context(ctx); return -1; } uci_foreach_element(&pkg->sections, e) { struct uci_section *s = uci_to_section(e); if (!strcmp(s->type, use_ptm ? "ptm-device" : "atm-device")) { value = uci_lookup_option_string(ctx, s, "device"); if (value) { if (snprintf(ifname, len, "%s", value) >= len) { // truncated uci_free_context(ctx); return -2; } // success uci_free_context(ctx); return 0; } } } // not found uci_free_context(ctx); return -1; } static int uci_get_oem_params(char *vendor_id, char *sw_version, char *serial_nr) { struct uci_context *ctx; struct uci_package *pkg; struct uci_element *e; const char *value; ctx = uci_alloc_context(); if (!ctx) return -1; if (uci_load(ctx, "dsl", &pkg)) { uci_free_context(ctx); return -1; } uci_foreach_element(&pkg->sections, e) { struct uci_section *s = uci_to_section(e); if (!strcmp(s->type, "oem-parameters")) { value = uci_lookup_option_string(ctx, s, "country_code"); if (value) sscanf(value, "%2hhX%2hhX", vendor_id, vendor_id + 1); value = uci_lookup_option_string(ctx, s, "vendor_id"); if (value) strncpy(vendor_id + 2, value, 4); value = uci_lookup_option_string(ctx, s, "vendor_suffix"); if (value) sscanf(value, "%2hhX%2hhX", vendor_id + 6, vendor_id + 7); value = uci_lookup_option_string(ctx, s, "sw_version"); if (value) strncpy(sw_version, value, 16); value = uci_lookup_option_string(ctx, s, "serial_nr"); if (value) strncpy(serial_nr, value, 32); } } uci_free_context(ctx); return 0; } int dsl_oem_init() { if (xdsl_ops.set_oem_parameter) { char vendor_id[8] = { 0 }; // 2 byte hex country code + 4 byte string id + 2 byte hex suffix char sw_version[16] = { 0 }; char serial_nr[32] = { 0 }; char zero[32] = { 0 }; if (uci_get_oem_params(vendor_id, sw_version, serial_nr) != 0) { DSLMNGR_LOG(LOG_ERR, "Unable to read OEM parameters from config, skipping OEM init\n"); return -1; } // Note: These are BRCM specific parameters from bcmadsl.h if (memcmp(vendor_id, zero, 8) != 0) { (*xdsl_ops.set_oem_parameter)(4, (void *)vendor_id, 8); } if (memcmp(sw_version, zero, 16) != 0) { (*xdsl_ops.set_oem_parameter)(5, (void *)sw_version, 16); } if (memcmp(serial_nr, zero, 32) != 0) { (*xdsl_ops.set_oem_parameter)(6, (void *)serial_nr, 32); } } return 0; }