Support configuration of CON MO Groups/Paths from VTY

The code for supporting the configuration of the OM2000 CON (LAPD
Concentrator) MO was so far incomplete and not used from the OM2000 FSM
initialization.  This patch adds
* VTY commands for configuration of CON Groups and Paths
* The FSM integration to actually configure the CON MO

Change-Id: I56dc1b5e35adef3a2078bcf9536537eb0f454192
This commit is contained in:
Harald Welte 2016-11-11 19:41:59 +01:00
parent fe7be8ddd0
commit eae68292bd
5 changed files with 237 additions and 71 deletions

View File

@ -56,6 +56,39 @@ struct is_conn_group {
uint8_t ci;
};
/* on-wire format for CON Path */
struct om2k_con_path {
uint16_t ccp;
uint8_t ci;
uint8_t tag;
uint8_t tei;
} __attribute__ ((packed));
/* internal data format for CON group */
struct con_group {
/* links list of CON groups in BTS */
struct llist_head list;
struct gsm_bts *bts;
/* CON Group ID */
uint8_t cg;
/* list of CON paths in this group */
struct llist_head paths;
};
/* internal data format for CON path */
struct con_path {
/* links with con_group.paths */
struct llist_head list;
/* CON Connection Point */
uint16_t ccp;
/* Contiguity Index */
uint8_t ci;
/* Tag */
uint8_t tag;
/* TEI */
uint8_t tei;
};
extern const struct abis_om2k_mo om2k_mo_cf;
extern const struct abis_om2k_mo om2k_mo_is;
extern const struct abis_om2k_mo om2k_mo_con;

View File

@ -29,6 +29,7 @@ enum bsc_vty_node {
NAT_BSC_NODE,
MSC_NODE,
OM2K_NODE,
OM2K_CON_GROUP_NODE,
TRUNK_NODE,
PGROUP_NODE,
MNCC_INT_NODE,

View File

@ -1132,21 +1132,49 @@ int abis_om2k_tx_is_conf_req(struct gsm_bts *bts)
return abis_om2k_sendmsg(bts, msg);
}
int abis_om2k_tx_con_conf_req(struct gsm_bts *bts, uint8_t *data,
unsigned int len)
int abis_om2k_tx_con_conf_req(struct gsm_bts *bts)
{
struct msgb *msg = om2k_msgb_alloc();
struct abis_om2k_hdr *o2k;
struct con_group *grp;
unsigned int num_grps = 0;
o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k));
/* count number of groups in linked list */
llist_for_each_entry(grp, &bts->rbs2000.con.conn_groups, list)
num_grps++;
if (!num_grps)
return -EINVAL;
/* first build the value part of the OM2K_DEI_CON_CONN_LIST DEI */
msgb_put_u8(msg, num_grps);
llist_for_each_entry(grp, &bts->rbs2000.con.conn_groups, list) {
struct con_path *cp;
unsigned int num_paths = 0;
llist_for_each_entry(cp, &grp->paths, list)
num_paths++;
msgb_put_u8(msg, num_paths);
llist_for_each_entry(cp, &grp->paths, list) {
struct om2k_con_path *om2k_cp;
om2k_cp = (struct om2k_con_path *) msgb_put(msg, sizeof(*om2k_cp));
om2k_cp->ccp = htons(cp->ccp);
om2k_cp->ci = cp->ci;
om2k_cp->tag = cp->tag;
om2k_cp->tei = cp->tei;
}
}
msgb_push_u8(msg, msgb_length(msg));
msgb_push_u8(msg, OM2K_DEI_CON_CONN_LIST);
/* pre-pend the list number DEIs */
msgb_tv_push(msg, OM2K_DEI_END_LIST_NR, 1);
msgb_tv_push(msg, OM2K_DEI_LIST_NR, 1);
/* pre-pend the OM2K header */
o2k = (struct abis_om2k_hdr *) msgb_push(msg, sizeof(*o2k));
fill_om2k_hdr(o2k, &bts->rbs2000.con.om2k_mo.addr,
OM2K_MSGT_CON_CONF_REQ);
msgb_tv_put(msg, OM2K_DEI_LIST_NR, 1);
msgb_tv_put(msg, OM2K_DEI_END_LIST_NR, 1);
msgb_tlv_put(msg, OM2K_DEI_CON_CONN_LIST, len, data);
DEBUGP(DNM, "Tx MO=%s %s\n",
om2k_mo_name(&bts->rbs2000.con.om2k_mo.addr),
get_value_string(om2k_msgcode_vals, OM2K_MSGT_CON_CONF_REQ));
@ -1566,8 +1594,7 @@ static void om2k_mo_st_wait_start_res(struct osmo_fsm_inst *fi, uint32_t event,
abis_om2k_tx_is_conf_req(omfp->trx->bts);
break;
case OM2K_MO_CLS_CON:
/* TODO */
//abis_om2k_tx_con_conf_req(omfp->trx->bts, data, len);
abis_om2k_tx_con_conf_req(omfp->trx->bts);
break;
case OM2K_MO_CLS_TX:
abis_om2k_tx_tx_conf_req(omfp->trx);
@ -2061,6 +2088,7 @@ enum om2k_bts_event {
OM2K_BTS_EVT_START,
OM2K_BTS_EVT_CF_DONE,
OM2K_BTS_EVT_IS_DONE,
OM2K_BTS_EVT_CON_DONE,
OM2K_BTS_EVT_TF_DONE,
OM2K_BTS_EVT_TRX_DONE,
OM2K_BTS_EVT_STOP,
@ -2070,6 +2098,7 @@ static const struct value_string om2k_bts_events[] = {
{ OM2K_BTS_EVT_START, "START" },
{ OM2K_BTS_EVT_CF_DONE, "CF-DONE" },
{ OM2K_BTS_EVT_IS_DONE, "IS-DONE" },
{ OM2K_BTS_EVT_CON_DONE, "CON-DONE" },
{ OM2K_BTS_EVT_TF_DONE, "TF-DONE" },
{ OM2K_BTS_EVT_TRX_DONE, "TRX-DONE" },
{ OM2K_BTS_EVT_STOP, "STOP" },
@ -2080,6 +2109,7 @@ enum om2k_bts_state {
OM2K_BTS_S_INIT,
OM2K_BTS_S_WAIT_CF,
OM2K_BTS_S_WAIT_IS,
OM2K_BTS_S_WAIT_CON,
OM2K_BTS_S_WAIT_TF,
OM2K_BTS_S_WAIT_TRX,
OM2K_BTS_S_DONE,
@ -2121,6 +2151,18 @@ static void om2k_bts_s_wait_is(struct osmo_fsm_inst *fi, uint32_t event, void *d
struct gsm_bts *bts = obfp->bts;
OSMO_ASSERT(event == OM2K_BTS_EVT_IS_DONE);
osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_CON,
BTS_FSM_TIMEOUT, 0);
om2k_mo_fsm_start(fi, OM2K_BTS_EVT_CON_DONE, bts->c0,
&bts->rbs2000.con.om2k_mo);
}
static void om2k_bts_s_wait_con(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct om2k_bts_fsm_priv *obfp = fi->priv;
struct gsm_bts *bts = obfp->bts;
OSMO_ASSERT(event == OM2K_BTS_EVT_CON_DONE);
/* TF can take a long time to initialize, wait for 10min */
osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TF, 600, 0);
om2k_mo_fsm_start(fi, OM2K_BTS_EVT_TF_DONE, bts->c0,
@ -2178,10 +2220,17 @@ static const struct osmo_fsm_state om2k_bts_states[] = {
[OM2K_BTS_S_WAIT_IS] = {
.in_event_mask = S(OM2K_BTS_EVT_IS_DONE),
.out_state_mask = S(OM2K_BTS_S_ERROR) |
S(OM2K_BTS_S_WAIT_TF),
S(OM2K_BTS_S_WAIT_CON),
.name = "WAIT-IS",
.action = om2k_bts_s_wait_is,
},
[OM2K_BTS_S_WAIT_CON] = {
.in_event_mask = S(OM2K_BTS_EVT_CON_DONE),
.out_state_mask = S(OM2K_BTS_S_ERROR) |
S(OM2K_BTS_S_WAIT_TF),
.name = "WAIT-CON",
.action = om2k_bts_s_wait_con,
},
[OM2K_BTS_S_WAIT_TF] = {
.in_event_mask = S(OM2K_BTS_EVT_TF_DONE),
.out_state_mask = S(OM2K_BTS_S_ERROR) |

View File

@ -48,9 +48,18 @@ static struct cmd_node om2k_node = {
1,
};
static struct cmd_node om2k_con_group_node = {
OM2K_CON_GROUP_NODE,
"%s(om2k-con-group)# ",
1,
};
struct con_group;
struct oml_node_state {
struct gsm_bts *bts;
struct abis_om2k_mo mo;
struct con_group *cg;
};
static int dummy_config_write(struct vty *v)
@ -246,65 +255,113 @@ DEFUN(om2k_cap_req, om2k_cap_req_cmd,
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)
static struct con_group *con_group_find_or_create(struct gsm_bts *bts, uint8_t cg)
{
struct con_conn_group *ent = talloc_zero(bts, struct con_conn_group);
struct con_group *ent;
llist_for_each_entry(ent, &bts->rbs2000.con.conn_groups, list) {
if (ent->cg == cg)
return ent;
}
ent = talloc_zero(bts, struct con_group);
ent->bts = bts;
ent->cg = cg;
ent->ccp = ccp;
ent->tag = tag;
ent->tei = tei;
INIT_LLIST_HEAD(&ent->paths);
llist_add_tail(&ent->list, &bts->rbs2000.con.conn_groups);
return ent;
}
static int del_con_list(struct gsm_bts *bts, uint8_t cg, uint16_t ccp,
uint8_t tag, uint8_t tei)
static int con_group_del(struct gsm_bts *bts, uint8_t cg_id)
{
struct con_conn_group *grp, *grp2;
struct con_group *cg, *cg2;
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);
llist_for_each_entry_safe(cg, cg2, &bts->rbs2000.con.conn_groups, list) {
if (cg->cg == cg_id) {
llist_del(&cg->list);
talloc_free(cg);
return 0;
};
}
return -ENOENT;
}
static void con_group_add_path(struct con_group *cg, uint16_t ccp,
uint8_t ci, uint8_t tag, uint8_t tei)
{
struct con_path *cp = talloc_zero(cg, struct con_path);
cp->ccp = ccp;
cp->ci = ci;
cp->tag = tag;
cp->tei = tei;
llist_add(&cp->list, &cg->paths);
}
static int con_group_del_path(struct con_group *cg, uint16_t ccp,
uint8_t ci, uint8_t tag, uint8_t tei)
{
struct con_path *cp, *cp2;
llist_for_each_entry_safe(cp, cp2, &cg->paths, list) {
if (cp->ccp == ccp && cp->ci == ci && cp->tag == tag &&
cp->tei == tei) {
llist_del(&cp->list);
talloc_free(cp);
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")
DEFUN(cfg_om2k_con_group, cfg_om2k_con_group_cmd,
"con-connection-group <1-31>",
"Configure a CON (Concentrator) Connection Group\n"
"CON Connection Group 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]);
struct gsm_bts *bts = vty->index;
struct con_group *cg;
uint8_t cgid = atoi(argv[0]);
if (bts->type != GSM_BTS_TYPE_RBS2000) {
vty_out(vty, "%% CON MO only exists in RBS2000%s",
VTY_NEWLINE);
return CMD_WARNING;
}
cg = con_group_find_or_create(bts, cgid);
if (!cg) {
vty_out(vty, "%% Cannot create CON Group%s",
VTY_NEWLINE);
return CMD_WARNING;
}
vty->node = OM2K_CON_GROUP_NODE;
vty->index = cg;
return CMD_SUCCESS;
}
#define CON_PATH_HELP "CON Path (In/Out)\n" \
"Add CON Path to Concentration Group\n" \
"Delete CON Path from Concentration Group\n" \
"CON Conection Point\n" \
"Contiguity Index\n" \
DEFUN(cfg_om2k_con_path_dec, cfg_om2k_con_path_dec_cmd,
"con-path (add|del) <0-1023> <0-7> deconcentrated <0-63>",
CON_PATH_HELP "De-concentrated in/outlet\n" "TEI Value\n")
{
struct con_group *cg = vty->index;
uint16_t ccp = atoi(argv[1]);
uint8_t ci = atoi(argv[2]);
uint8_t tei = atoi(argv[3]);
if (!strcmp(argv[0], "add"))
add_con_list(bts, cg, ccp, 0, 0xff);
con_group_add_path(cg, ccp, ci, 0, tei);
else {
if (del_con_list(bts, cg, ccp, 0, 0xff) < 0) {
vty_out(vty, "%% No matching CON list entry%s",
if (con_group_del_path(cg, ccp, ci, 0, tei) < 0) {
vty_out(vty, "%% No matching CON Path%s",
VTY_NEWLINE);
return CMD_WARNING;
}
@ -313,20 +370,19 @@ DEFUN(om2k_con_list_dec, om2k_con_list_dec_cmd,
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")
DEFUN(cfg_om2k_con_path_conc, cfg_om2k_con_path_conc_cmd,
"con-path (add|del) <0-1023> <0-7> concentrated <1-16>",
CON_PATH_HELP "Concentrated in/outlet\n" "Tag 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]);
struct con_group *cg = vty->index;
uint16_t ccp = atoi(argv[1]);
uint8_t ci = atoi(argv[2]);
uint8_t tag = atoi(argv[3]);
if (!strcmp(argv[0], "add"))
add_con_list(bts, cg, ccp, cg, tei);
con_group_add_path(cg, ccp, ci, tag, 0xff);
else {
if (del_con_list(bts, cg, ccp, cg, tei) < 0) {
if (con_group_del_path(cg, ccp, ci, tag, 0xff) < 0) {
vty_out(vty, "%% No matching CON list entry%s",
VTY_NEWLINE);
return CMD_WARNING;
@ -437,22 +493,35 @@ DEFUN(om2k_conf_req, om2k_conf_req_cmd,
return CMD_SUCCESS;
}
static void dump_con_group(struct vty *vty, struct con_group *cg)
{
struct con_path *cp;
llist_for_each_entry(cp, &cg->paths, list) {
vty_out(vty, " con-path add %u %u ", cp->ccp, cp->ci);
if (cp->tei == 0xff) {
vty_out(vty, "concentrated %u%s", cp->tag,
VTY_NEWLINE);
} else {
vty_out(vty, "deconcentrated %u%s", cp->tei,
VTY_NEWLINE);
}
}
}
void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts)
{
struct is_conn_group *igrp;
struct con_conn_group *cgrp;
struct con_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);
vty_out(vty, " con-connection-group %u%s", cgrp->cg,
VTY_NEWLINE);
dump_con_group(vty, cgrp);
}
}
@ -474,10 +543,14 @@ int abis_om2k_vty_init(void)
install_element(OM2K_NODE, &om2k_test_cmd);
install_element(OM2K_NODE, &om2k_cap_req_cmd);
install_element(OM2K_NODE, &om2k_conf_req_cmd);
install_element(OM2K_NODE, &om2k_con_list_dec_cmd);
install_element(OM2K_NODE, &om2k_con_list_tei_cmd);
install_node(&om2k_con_group_node, dummy_config_write);
vty_install_default(OM2K_CON_GROUP_NODE);
install_element(OM2K_CON_GROUP_NODE, &cfg_om2k_con_path_dec_cmd);
install_element(OM2K_CON_GROUP_NODE, &cfg_om2k_con_path_conc_cmd);
install_element(BTS_NODE, &cfg_bts_is_conn_list_cmd);
install_element(BTS_NODE, &cfg_om2k_con_group_cmd);
return 0;
}

View File

@ -28,6 +28,7 @@
#include <openbsc/debug.h>
#include <openbsc/gsm_subscriber.h>
#include <openbsc/bsc_nat.h>
#include <openbsc/abis_om2000.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/command.h>
@ -79,6 +80,15 @@ int bsc_vty_go_parent(struct vty *vty)
talloc_free(vty->index);
vty->index = NULL;
break;
case OM2K_CON_GROUP_NODE:
vty->node = BTS_NODE;
{
struct con_group *cg = vty->index;
struct gsm_bts *bts = cg->bts;
vty->index = bts;
vty->index_sub = &bts->description;
}
break;
case NAT_BSC_NODE:
vty->node = NAT_NODE;
{