/* VTY interface for A-bis OM2000 */ /* (C) 2010-2011 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern struct gsm_network *bsc_gsmnet; static struct cmd_node om2k_node = { OM2K_NODE, "%s(om2k)# ", 1, }; struct oml_node_state { struct gsm_bts *bts; struct abis_om2k_mo mo; }; static int dummy_config_write(struct vty *v) { return CMD_SUCCESS; } /* FIXME: auto-generate those strings from the value_string lists */ #define OM2K_OBJCLASS_VTY "(trxc|ts|tf|is|con|dp|cf|tx|rx)" #define OM2K_OBJCLASS_VTY_HELP "TRX Controller\n" \ "Timeslot\n" \ "Timing Function\n" \ "Interface Switch\n" \ "Abis Concentrator\n" \ "Digital Path\n" \ "Central Function\n" \ "Transmitter\n" \ "Receiver\n" DEFUN(om2k_class_inst, om2k_class_inst_cmd, "bts <0-255> om2000 class " OM2K_OBJCLASS_VTY " <0-255> <0-255> <0-255>", "BTS related commands\n" "BTS Number\n" "Manipulate the OM2000 managed objects\n" "Object Class\n" OM2K_OBJCLASS_VTY_HELP "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") { struct gsm_bts *bts; struct oml_node_state *oms; int bts_nr = atoi(argv[0]); bts = gsm_bts_num(bsc_gsmnet, bts_nr); if (!bts) { vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } if (bts->type != GSM_BTS_TYPE_RBS2000) { vty_out(vty, "%% BTS %d not an Ericsson RBS%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); if (!oms) return CMD_WARNING; oms->bts = bts; oms->mo.class = get_string_value(om2k_mo_class_short_vals, argv[1]); oms->mo.bts = atoi(argv[2]); oms->mo.assoc_so = atoi(argv[3]); oms->mo.inst = atoi(argv[4]); vty->index = oms; vty->node = OM2K_NODE; return CMD_SUCCESS; } DEFUN(om2k_classnum_inst, om2k_classnum_inst_cmd, "bts <0-255> om2000 class <0-255> <0-255> <0-255> <0-255>", "BTS related commands\n" "BTS Number\n" "Manipulate the OML managed objects\n" "Object Class\n" "Object Class\n" "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") { struct gsm_bts *bts; struct oml_node_state *oms; int bts_nr = atoi(argv[0]); bts = gsm_bts_num(bsc_gsmnet, bts_nr); if (!bts) { vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); if (!oms) return CMD_WARNING; oms->bts = bts; oms->mo.class = atoi(argv[1]); oms->mo.bts = atoi(argv[2]); oms->mo.assoc_so = atoi(argv[3]); oms->mo.inst = atoi(argv[4]); vty->index = oms; vty->node = OM2K_NODE; return CMD_SUCCESS; } DEFUN(om2k_reset, om2k_reset_cmd, "reset-command", "Reset the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_reset_cmd(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_start, om2k_start_cmd, "start-request", "Start the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_start_req(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_status, om2k_status_cmd, "status-request", "Get the MO Status\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_status_req(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_connect, om2k_connect_cmd, "connect-command", "Connect the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_connect_cmd(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_disconnect, om2k_disconnect_cmd, "disconnect-command", "Disconnect the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_disconnect_cmd(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_enable, om2k_enable_cmd, "enable-request", "Enable the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_enable_req(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_disable, om2k_disable_cmd, "disable-request", "Disable the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_disable_req(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_op_info, om2k_op_info_cmd, "operational-info <0-1>", "Set operational information\n") { struct oml_node_state *oms = vty->index; int oper = atoi(argv[0]); abis_om2k_tx_op_info(oms->bts, &oms->mo, oper); return CMD_SUCCESS; } DEFUN(om2k_test, om2k_test_cmd, "test-request", "Test the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_test_req(oms->bts, &oms->mo); return CMD_SUCCESS; } struct con_conn_group { struct llist_head list; uint8_t cg; uint16_t ccp; uint8_t tag; uint8_t tei; }; static void add_con_list(struct gsm_bts *bts, uint8_t cg, uint16_t ccp, uint8_t tag, uint8_t tei) { struct con_conn_group *ent = talloc_zero(bts, struct con_conn_group); ent->cg = cg; ent->ccp = ccp; ent->tag = tag; ent->tei = tei; llist_add_tail(&ent->list, &bts->rbs2000.con.conn_groups); } static int del_con_list(struct gsm_bts *bts, uint8_t cg, uint16_t ccp, uint8_t tag, uint8_t tei) { struct con_conn_group *grp, *grp2; llist_for_each_entry_safe(grp, grp2, &bts->rbs2000.con.conn_groups, list) { if (grp->cg == cg && grp->ccp == ccp && grp->tag == tag && grp->tei == tei) { llist_del(&grp->list); talloc_free(grp); return 0; } } return -ENOENT; } #define CON_LIST_HELP "CON connetiton list\n" \ "Add entry to CON list\n" \ "Delete entry from CON list\n" \ "Connection Group Number\n" \ "CON Connection Point\n" \ DEFUN(om2k_con_list_dec, om2k_con_list_dec_cmd, "con-connection-list (add|del) <1-255> <0-1023> deconcentrated", CON_LIST_HELP "De-concentrated in/outlet\n") { struct oml_node_state *oms = vty->index; struct gsm_bts *bts = oms->bts; uint8_t cg = atoi(argv[1]); uint16_t ccp = atoi(argv[2]); if (!strcmp(argv[0], "add")) add_con_list(bts, cg, ccp, 0, 0xff); else { if (del_con_list(bts, cg, ccp, 0, 0xff) < 0) { vty_out(vty, "%% No matching CON list entry%s", VTY_NEWLINE); return CMD_WARNING; } } return CMD_SUCCESS; } DEFUN(om2k_con_list_tei, om2k_con_list_tei_cmd, "con-connection-list (add|del) <1-255> <0-1023> tei <0-63>", CON_LIST_HELP "Concentrated in/outlet with TEI\n" "TEI Number\n") { struct oml_node_state *oms = vty->index; struct gsm_bts *bts = oms->bts; uint8_t cg = atoi(argv[1]); uint16_t ccp = atoi(argv[2]); uint8_t tei = atoi(argv[3]); if (!strcmp(argv[0], "add")) add_con_list(bts, cg, ccp, cg, tei); else { if (del_con_list(bts, cg, ccp, cg, tei) < 0) { vty_out(vty, "%% No matching CON list entry%s", VTY_NEWLINE); return CMD_WARNING; } } return CMD_SUCCESS; } static void om2k_fill_is_conn_grp(struct om2k_is_conn_grp *grp, uint16_t icp1, uint16_t icp2, uint8_t cont_idx) { grp->icp1 = htons(icp1); grp->icp2 = htons(icp2); grp->cont_idx = cont_idx; } struct is_conn_group { struct llist_head list; uint16_t icp1; uint16_t icp2; uint8_t ci; }; DEFUN(cfg_bts_is_conn_list, cfg_bts_is_conn_list_cmd, "is-connection-list (add|del) <0-2047> <0-2047> <0-255>", "Interface Switch Connnection List\n" "Add to IS list\n" "Delete from IS list\n" "ICP1\n" "ICP2\n" "Contiguity Index\n") { struct gsm_bts *bts = vty->index; uint16_t icp1 = atoi(argv[1]); uint16_t icp2 = atoi(argv[2]); uint8_t ci = atoi(argv[3]); struct is_conn_group *grp, *grp2; if (!strcmp(argv[0], "add")) { grp = talloc_zero(bts, struct is_conn_group); grp->icp1 = icp1; grp->icp2 = icp2; grp->ci = ci; llist_add_tail(&grp->list, &bts->rbs2000.is.conn_groups); } else { llist_for_each_entry_safe(grp, grp2, &bts->rbs2000.is.conn_groups, list) { if (grp->icp1 == icp1 && grp->icp2 == icp2 && grp->ci == ci) { llist_del(&grp->list); talloc_free(grp); return CMD_SUCCESS; } } vty_out(vty, "%% No matching IS Conn Group found!%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(om2k_is_conf_req, om2k_is_conf_req_cmd, "is-conf-req", "Send IS Configuration Request\n") { struct oml_node_state *oms = vty->index; struct gsm_bts *bts = oms->bts; struct is_conn_group *grp; unsigned int num_grps = 0, i = 0; struct om2k_is_conn_grp *o2grps; /* count number of groups in linked list */ llist_for_each_entry(grp, &bts->rbs2000.is.conn_groups, list) num_grps++; if (!num_grps) { vty_out(vty, "%% No IS connection groups configured!%s", VTY_NEWLINE); return CMD_WARNING; } /* allocate buffer for oml group array */ o2grps = talloc_zero_array(bts, struct om2k_is_conn_grp, num_grps); /* fill array with data from linked list */ llist_for_each_entry(grp, &bts->rbs2000.is.conn_groups, list) om2k_fill_is_conn_grp(&o2grps[i++], grp->icp1, grp->icp2, grp->ci); /* send the actual OML request */ abis_om2k_tx_is_conf_req(oms->bts, o2grps, num_grps); talloc_free(o2grps); return CMD_SUCCESS; } void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts) { struct is_conn_group *igrp; struct con_conn_group *cgrp; llist_for_each_entry(igrp, &bts->rbs2000.is.conn_groups, list) vty_out(vty, " is-connection-list add %u %u %u%s", igrp->icp1, igrp->icp2, igrp->ci, VTY_NEWLINE); llist_for_each_entry(cgrp, &bts->rbs2000.con.conn_groups, list) { vty_out(vty, " con-connection-list add %u %u ", cgrp->cg, cgrp->ccp); if (cgrp->tei == 0xff) vty_out(vty, "deconcentrated%s", VTY_NEWLINE); else vty_out(vty, "tei %u%s", cgrp->tei, VTY_NEWLINE); } } int abis_om2k_vty_init(void) { install_element(ENABLE_NODE, &om2k_class_inst_cmd); install_element(ENABLE_NODE, &om2k_classnum_inst_cmd); install_node(&om2k_node, dummy_config_write); install_default(OM2K_NODE); install_element(OM2K_NODE, &ournode_exit_cmd); install_element(OM2K_NODE, &om2k_reset_cmd); install_element(OM2K_NODE, &om2k_start_cmd); install_element(OM2K_NODE, &om2k_status_cmd); install_element(OM2K_NODE, &om2k_connect_cmd); install_element(OM2K_NODE, &om2k_disconnect_cmd); install_element(OM2K_NODE, &om2k_enable_cmd); install_element(OM2K_NODE, &om2k_disable_cmd); install_element(OM2K_NODE, &om2k_op_info_cmd); install_element(OM2K_NODE, &om2k_test_cmd); install_element(OM2K_NODE, &om2k_is_conf_req_cmd); install_element(OM2K_NODE, &om2k_con_list_dec_cmd); install_element(OM2K_NODE, &om2k_con_list_tei_cmd); install_element(BTS_NODE, &cfg_bts_is_conn_list_cmd); return 0; }