forked from retronetworking/osmo-v5
Move v5x_data.c and v5x_internal.h to src/
Also split VTY code into v5x_vty.c and v5x_vty.h Some VTY code from main.c is moved also.
This commit is contained in:
parent
83d8cbdd3a
commit
33eea99531
|
@ -8,6 +8,7 @@ bin_PROGRAMS = osmo-v5-le
|
|||
osmo_v5_le_SOURCES = logging.c \
|
||||
ph_socket.c \
|
||||
v5x_data.c \
|
||||
v5x_vty.c \
|
||||
lapv5.c \
|
||||
v5x_protocol.c \
|
||||
v5x_l1_fsm.c \
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
#include "v5x_internal.h"
|
||||
#include "v5x_protocol.h"
|
||||
#include "lapv5.h"
|
||||
#include "v5x_le_ctrl_fsm.h"
|
||||
#include "v5x_le_port_fsm.h"
|
||||
#include "v5x_le_pstn_fsm.h"
|
||||
#include "v5x_le_management.h"
|
||||
#include "logging.h"
|
||||
|
||||
static LLIST_HEAD(v5_instances);
|
||||
|
||||
struct v5x_instance *v5x_instance_alloc(void *ctx)
|
||||
{
|
||||
struct v5x_instance *v5i = talloc_zero(ctx, struct v5x_instance);
|
||||
if (!v5i)
|
||||
return NULL;
|
||||
|
||||
INIT_LLIST_HEAD(&v5i->interfaces);
|
||||
|
||||
llist_add_tail(&v5i->list, &v5_instances);
|
||||
|
||||
return v5i;
|
||||
}
|
||||
|
||||
static void v5x_ts_init(struct v5x_timeslot *v5ts, uint8_t nr, struct v5x_link *v5l, uint16_t l3_addr)
|
||||
{
|
||||
v5ts->nr = nr;
|
||||
v5ts->link = v5l;
|
||||
v5ts->l3_address = l3_addr;
|
||||
}
|
||||
|
||||
static void v5x_cchan_init(struct v5x_c_channel *v5cc, struct v5x_link *v5l, struct v5x_timeslot *v5ts, bool active)
|
||||
{
|
||||
v5cc->link = v5l;
|
||||
v5cc->ts = v5ts;
|
||||
v5cc->active = active;
|
||||
}
|
||||
|
||||
static void v5x_link_init(struct v5x_link *v5l, struct v5x_interface *v5if, uint8_t id)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
v5l->id = id;
|
||||
v5l->interface = v5if;
|
||||
|
||||
for (i = 1; i < ARRAY_SIZE(v5l->ts); i++) {
|
||||
v5x_ts_init(&v5l->ts[i], i, v5l, 0 /*l3_addr*/);
|
||||
}
|
||||
|
||||
/* primary c-channel must always be present */
|
||||
v5x_cchan_init(&v5l->c_channel[0], v5l, &v5l->ts[16], true);
|
||||
}
|
||||
|
||||
struct v5x_interface *v5x_interface_alloc(struct v5x_instance *v5i, enum v5x_dialect dialect,
|
||||
int (*ph_data_req_cb)(struct msgb *msg, void *cbdata))
|
||||
{
|
||||
struct v5x_interface *v5if = talloc_zero(v5i, struct v5x_interface);
|
||||
//struct v5x_link *v5l;
|
||||
struct v5x_c_channel *primary_c_chan;
|
||||
|
||||
v5if->instance = v5i;
|
||||
v5if->dialect = dialect;
|
||||
|
||||
/* primary link must alwasy be present */
|
||||
v5x_link_init(&v5if->links[0], v5if, 0);
|
||||
v5if->primary_link = &v5if->links[0];
|
||||
|
||||
primary_c_chan = &v5if->primary_link->c_channel[0];
|
||||
|
||||
v5if->control.ctrl = v5x_le_ctrl_create(v5if, NULL, 0);
|
||||
if (!v5if->control.ctrl)
|
||||
return NULL;
|
||||
v5if->control.li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if,
|
||||
&lapd_profile_lapv5dl, "control");
|
||||
v5if->control.c_chan = primary_c_chan;
|
||||
|
||||
v5if->pstn.li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if,
|
||||
&lapd_profile_lapv5dl, "pstn");
|
||||
v5if->pstn.c_chan = primary_c_chan;
|
||||
|
||||
if (v5if->dialect == V5X_DIALECT_V52) {
|
||||
v5if->lcp.li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if,
|
||||
&lapd_profile_lapv5dl, "lcp");
|
||||
v5if->lcp.c_chan = primary_c_chan;
|
||||
|
||||
v5if->bcc.li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if,
|
||||
&lapd_profile_lapv5dl, "bcc");
|
||||
v5if->bcc.c_chan = primary_c_chan;
|
||||
|
||||
v5if->protection[0].li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if,
|
||||
&lapd_profile_lapv5dl, "protection0");
|
||||
v5if->protection[0].c_chan = primary_c_chan;
|
||||
|
||||
//protection[1] ?
|
||||
}
|
||||
|
||||
INIT_LLIST_HEAD(&v5if->user_ports);
|
||||
|
||||
llist_add_tail(&v5if->list, &v5i->interfaces);
|
||||
|
||||
return v5if;
|
||||
}
|
||||
|
||||
struct v5x_user_port *v5x_user_port_create(struct v5x_interface *v5if, uint16_t nr, enum v5x_user_type type,
|
||||
uint8_t ts1, uint8_t ts2)
|
||||
{
|
||||
struct v5x_user_port *v5up;
|
||||
int rc;
|
||||
|
||||
if ((v5up = v5if->links[0].ts[ts1].v5up)) {
|
||||
LOGP(DV5, LOGL_ERROR, "Time slot %d is already assigned to another user port\n", ts1);
|
||||
return NULL;
|
||||
}
|
||||
if (type == V5X_USER_TYPE_ISDN) {
|
||||
if ((v5up = v5if->links[0].ts[ts2].v5up)) {
|
||||
LOGP(DV5, LOGL_ERROR, "Time slot %d is already assigned to another user port\n", ts2);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
v5up = talloc_zero(v5if, struct v5x_user_port);
|
||||
if (!v5up)
|
||||
return NULL;
|
||||
|
||||
llist_add_tail(&v5up->list, &v5if->user_ports);
|
||||
|
||||
v5up->inst = v5if;
|
||||
v5up->nr = nr;
|
||||
v5up->type = type;
|
||||
|
||||
/* assign ports */
|
||||
if (v5if->dialect == V5X_DIALECT_V51) {
|
||||
OSMO_ASSERT(ts1 >= 1 && ts1 <= 31);
|
||||
v5if->links[0].ts[ts1].v5up = v5up;
|
||||
v5up->ts_nr[0] = ts1;
|
||||
if (type == V5X_USER_TYPE_ISDN) {
|
||||
OSMO_ASSERT(ts2 >= 1 && ts2 <= 31);
|
||||
v5if->links[0].ts[ts2].v5up = v5up;
|
||||
v5up->ts_nr[1] = ts2;
|
||||
}
|
||||
}
|
||||
|
||||
v5up->ctrl = v5x_le_ctrl_create(NULL, v5up, nr);
|
||||
if (!v5up->ctrl) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create control protocol\n");
|
||||
v5x_user_port_destroy(v5up);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case V5X_USER_TYPE_ISDN:
|
||||
LOGP(DV5, LOGL_NOTICE, "Creating V5 ISDN user port with L3 addr %d\n", nr);
|
||||
sprintf(v5up->ifname, "isdn-%d", nr);
|
||||
OSMO_ASSERT(nr <= 8175);
|
||||
v5up->port_fi = v5x_le_port_isdn_create(v5up, nr);
|
||||
if (!v5up->port_fi) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create port control\n");
|
||||
v5x_user_port_destroy(v5up);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case V5X_USER_TYPE_PSTN:
|
||||
LOGP(DV5, LOGL_NOTICE, "Creating V5 PSTN user port with L3 addr %d\n", nr);
|
||||
sprintf(v5up->ifname, "pstn-%d", nr);
|
||||
OSMO_ASSERT(nr <= 32767);
|
||||
v5up->port_fi = v5x_le_port_pstn_create(v5up, nr);
|
||||
if (!v5up->port_fi) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create port control\n");
|
||||
v5x_user_port_destroy(v5up);
|
||||
return NULL;
|
||||
}
|
||||
v5up->pstn.proto = v5x_le_pstn_create(v5up, nr);
|
||||
if (!v5up->pstn.proto) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create PSTN protocol\n");
|
||||
v5x_user_port_destroy(v5up);
|
||||
return NULL;
|
||||
}
|
||||
/* bring port into service */
|
||||
v5x_le_pstn_mdu_snd(v5up, MDU_CTRL_port_blocked);
|
||||
break;
|
||||
}
|
||||
|
||||
rc = ph_socket_init(&v5up->ph_socket, ph_socket_rx_cb, v5up, v5up->ifname, 1);
|
||||
if (rc < 0) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create PH-socket\n");
|
||||
v5x_user_port_destroy(v5up);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return v5up;
|
||||
}
|
||||
|
||||
void v5x_user_port_destroy(struct v5x_user_port *v5up)
|
||||
{
|
||||
struct v5x_interface *v5if = v5up->inst;
|
||||
|
||||
LOGP(DV5, LOGL_NOTICE, "Destroying V5 user port with L3 addr %d\n", v5up->nr);
|
||||
|
||||
/* close fitst, because it sends messages */
|
||||
ph_socket_exit(&v5up->ph_socket);
|
||||
|
||||
/* unassign ports */
|
||||
if (v5up->ts_nr[0])
|
||||
v5if->links[v5up->link_nr[0]].ts[v5up->ts_nr[0]].v5up = NULL;
|
||||
if (v5up->ts_nr[1])
|
||||
v5if->links[v5up->link_nr[1]].ts[v5up->ts_nr[1]].v5up = NULL;
|
||||
|
||||
if (v5up->ctrl)
|
||||
v5x_le_ctrl_destroy(v5up->ctrl);
|
||||
|
||||
if (v5up->port_fi)
|
||||
osmo_fsm_inst_free(v5up->port_fi);
|
||||
|
||||
if (v5up->pstn.proto)
|
||||
v5x_le_pstn_destroy(v5up->pstn.proto);
|
||||
|
||||
llist_del(&v5up->list);
|
||||
|
||||
talloc_free(v5up);
|
||||
}
|
||||
|
||||
struct v5x_user_port *v5x_user_port_find(struct v5x_interface *v5if, uint16_t nr, bool is_isdn)
|
||||
{
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
llist_for_each_entry(v5up, &v5if->user_ports, list) {
|
||||
if (v5up->nr == nr && is_isdn == (v5up->type == V5X_USER_TYPE_ISDN))
|
||||
return v5up;
|
||||
}
|
||||
return NULL;
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
|
@ -24,10 +25,12 @@
|
|||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/gsm/lapd_core.h>
|
||||
|
||||
#include "ph_socket.h"
|
||||
|
||||
/* Table 35/G.964 */
|
||||
|
@ -202,8 +205,10 @@ struct v5x_instance {
|
|||
|
||||
struct v5x_instance *v5x_instance_alloc(void *ctx);
|
||||
struct v5x_interface *v5x_interface_alloc(struct v5x_instance *v5i, enum v5x_dialect dialect,
|
||||
uint32_t id, uint8_t variant, int (*ph_data_req_cb)(struct msgb *msg, void *cbdata));
|
||||
struct v5x_user_port *v5x_user_port_create(struct v5x_interface *v5if, uint16_t nr, enum v5x_user_type, uint8_t ts1, uint8_t ts2);
|
||||
int (*ph_data_req_cb)(struct msgb *msg, void *cbdata));
|
||||
struct v5x_user_port *v5x_user_port_create(struct v5x_interface *v5if, uint16_t nr, enum v5x_user_type,
|
||||
uint8_t ts1, uint8_t ts2);
|
||||
struct v5x_user_port *v5x_user_port_find(struct v5x_interface *v5if, uint16_t nr, bool is_isdn);
|
||||
void v5x_user_port_destroy(struct v5x_user_port *v5up);
|
||||
int v5x_vty_init(void);
|
||||
//FIXME: move this
|
||||
void ph_socket_rx_cb(ph_socket_t *s, int channel, uint8_t prim, uint8_t *data, int length);
|
|
@ -0,0 +1,188 @@
|
|||
|
||||
#include <osmocom/vty/command.h>
|
||||
|
||||
#include "v5x_internal.h"
|
||||
#include "v5x_vty.h"
|
||||
#include "../config.h"
|
||||
|
||||
extern struct v5x_instance *v5i;
|
||||
|
||||
enum v5_vty_node {
|
||||
INTERFACE_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
};
|
||||
|
||||
static struct cmd_node interface_node = {
|
||||
INTERFACE_NODE,
|
||||
"%s(config-if)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static int v5le_vty_is_config_node(struct vty *vty, int node)
|
||||
{
|
||||
switch (node) {
|
||||
case CONFIG_NODE:
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int v5le_vty_go_parent(struct vty *vty)
|
||||
{
|
||||
switch (vty->node) {
|
||||
default:
|
||||
if (v5le_vty_is_config_node(vty, vty->node))
|
||||
vty->node = CONFIG_NODE;
|
||||
else
|
||||
vty->node = ENABLE_NODE;
|
||||
|
||||
vty->index = NULL;
|
||||
}
|
||||
|
||||
return vty->node;
|
||||
}
|
||||
|
||||
struct vty_app_info vty_info = {
|
||||
.name = "OsmoV5LE",
|
||||
.version = PACKAGE_VERSION,
|
||||
.go_parent_cb = v5le_vty_go_parent,
|
||||
.is_config_node = v5le_vty_is_config_node,
|
||||
};
|
||||
|
||||
DEFUN(cfg_interface, cfg_interface_cmd,
|
||||
"interface", "Configure the V5 interface")
|
||||
{
|
||||
struct v5x_interface *v5if = (struct v5x_interface *)v5i->interfaces.next;
|
||||
|
||||
vty->node = INTERFACE_NODE;
|
||||
vty->index = v5if;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_interface_id, cfg_interface_id_cmd,
|
||||
"id <0-16777215>",
|
||||
"Set interface ID\n" "Interface ID")
|
||||
{
|
||||
struct v5x_interface *v5if = vty->index;
|
||||
|
||||
v5if->id = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_interface_variant, cfg_interface_variant_cmd,
|
||||
"variant <0-127>",
|
||||
"Set interface provisioning variant\n" "Variant value")
|
||||
{
|
||||
struct v5x_interface *v5if = vty->index;
|
||||
|
||||
v5if->variant = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_port_pstn, cfg_port_pstn_cmd,
|
||||
"port pstn <0-32767> <1-31>",
|
||||
"Create V5 user port\n" "PSTN user port\n" "L3 address\n" "Time slot")
|
||||
{
|
||||
struct v5x_interface *v5if = vty->index;
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
v5up = v5x_user_port_find(v5if, atoi(argv[0]), false);
|
||||
if (v5up) {
|
||||
vty_out(vty, "%%Given PSTN user port already exists, remove first.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
v5up = v5x_user_port_create(v5if, atoi(argv[0]), V5X_USER_TYPE_PSTN, atoi(argv[1]), 0);
|
||||
if (!v5up) {
|
||||
vty_out(vty, "%%Failed to create PSTN user port.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_no_port_pstn, cfg_no_port_pstn_cmd,
|
||||
"no port pstn <0-32767>",
|
||||
NO_STR "Delete V5 user port\n" "PSTN user port\n" "L3 address\n")
|
||||
{
|
||||
struct v5x_interface *v5if = vty->index;
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
v5up = v5x_user_port_find(v5if, atoi(argv[0]), false);
|
||||
if (!v5up) {
|
||||
vty_out(vty, "%%Given PSTN user port does not exist.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
v5x_user_port_destroy(v5up);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_port_isdn, cfg_port_isdn_cmd,
|
||||
"port isdn <0-8175> <1-31> <1-31>",
|
||||
"Create V5 user port\n" "ISDN user port\n" "L3 address\n" "Time slot 1\n" "Time slot 2\n")
|
||||
{
|
||||
struct v5x_interface *v5if = vty->index;
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
v5up = v5x_user_port_find(v5if, atoi(argv[0]), true);
|
||||
if (v5up) {
|
||||
vty_out(vty, "%%Given ISDN user port already exists, remove first.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
v5up = v5x_user_port_create(v5if, atoi(argv[0]), V5X_USER_TYPE_ISDN, atoi(argv[1]), atoi(argv[2]));
|
||||
if (!v5up) {
|
||||
vty_out(vty, "%%Failed to create ISDN user port.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_no_port_isdn, cfg_no_port_isdn_cmd,
|
||||
"no port isdn <0-8175>",
|
||||
NO_STR "Delete V5 user port\n" "ISDN user port\n" "L3 address\n")
|
||||
{
|
||||
struct v5x_interface *v5if = vty->index;
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
v5up = v5x_user_port_find(v5if, atoi(argv[0]), true);
|
||||
if (!v5up) {
|
||||
vty_out(vty, "%%Given ISDN user port does not exist.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
v5x_user_port_destroy(v5up);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static int config_write_interface(struct vty *vty)
|
||||
{
|
||||
struct v5x_interface *v5if = (struct v5x_interface *)v5i->interfaces.next;
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
vty_out(vty, "!%s", VTY_NEWLINE);
|
||||
vty_out(vty, "interface%s", VTY_NEWLINE);
|
||||
vty_out(vty, " id %d%s", v5if->id, VTY_NEWLINE);
|
||||
vty_out(vty, " variant %d%s", v5if->variant, VTY_NEWLINE);
|
||||
llist_for_each_entry(v5up, &v5if->user_ports, list) {
|
||||
switch (v5up->type) {
|
||||
case V5X_USER_TYPE_PSTN:
|
||||
vty_out(vty, " port pstn %d %d%s", v5up->nr, v5up->ts_nr[0], VTY_NEWLINE);
|
||||
break;
|
||||
case V5X_USER_TYPE_ISDN:
|
||||
vty_out(vty, " port isdn %d %d %d%s", v5up->nr, v5up->ts_nr[0], v5up->ts_nr[1], VTY_NEWLINE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int v5x_vty_init(void)
|
||||
{
|
||||
install_element(CONFIG_NODE, &cfg_interface_cmd);
|
||||
install_node(&interface_node, config_write_interface);
|
||||
install_element(INTERFACE_NODE, &cfg_interface_id_cmd);
|
||||
install_element(INTERFACE_NODE, &cfg_interface_variant_cmd);
|
||||
install_element(INTERFACE_NODE, &cfg_port_pstn_cmd);
|
||||
install_element(INTERFACE_NODE, &cfg_no_port_pstn_cmd);
|
||||
install_element(INTERFACE_NODE, &cfg_port_isdn_cmd);
|
||||
install_element(INTERFACE_NODE, &cfg_no_port_isdn_cmd);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
#pragma once
|
||||
|
||||
extern struct vty_app_info vty_info;
|
||||
int v5x_vty_init(void);
|
380
v5x_data.c
380
v5x_data.c
|
@ -1,380 +0,0 @@
|
|||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
#include "v5x_internal.h"
|
||||
#include "v5x_protocol.h"
|
||||
#include "v51_le_ctrl.h"
|
||||
#include "v52_le_user_port_fsm.h"
|
||||
#include "v5x_l2_mgmt.h"
|
||||
#include "v5x_le_pstn_fsm.h"
|
||||
#include "lapv5.h"
|
||||
#include "logging.h"
|
||||
|
||||
static LLIST_HEAD(v5_instances);
|
||||
|
||||
struct v5x_instance *v5x_instance_alloc(void *ctx)
|
||||
{
|
||||
struct v5x_instance *v5i = talloc_zero(ctx, struct v5x_instance);
|
||||
if (!v5i)
|
||||
return NULL;
|
||||
|
||||
INIT_LLIST_HEAD(&v5i->interfaces);
|
||||
|
||||
llist_add_tail(&v5i->list, &v5_instances);
|
||||
|
||||
return v5i;
|
||||
}
|
||||
|
||||
static void v5x_ts_init(struct v5x_timeslot *v5ts, uint8_t nr, struct v5x_link *v5l, uint16_t l3_addr)
|
||||
{
|
||||
v5ts->nr = nr;
|
||||
v5ts->link = v5l;
|
||||
v5ts->l3_address = l3_addr;
|
||||
}
|
||||
|
||||
static void v5x_cchan_init(struct v5x_c_channel *v5cc, struct v5x_link *v5l, struct v5x_timeslot *v5ts, bool active)
|
||||
{
|
||||
v5cc->link = v5l;
|
||||
v5cc->ts = v5ts;
|
||||
v5cc->active = active;
|
||||
}
|
||||
|
||||
static void v5x_link_init(struct v5x_link *v5l, struct v5x_interface *v5if, uint8_t id)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
v5l->id = id;
|
||||
v5l->interface = v5if;
|
||||
|
||||
for (i = 1; i < ARRAY_SIZE(v5l->ts); i++) {
|
||||
v5x_ts_init(&v5l->ts[i], i, v5l, 0 /*l3_addr*/);
|
||||
}
|
||||
|
||||
/* primary c-channel must always be present */
|
||||
v5x_cchan_init(&v5l->c_channel[0], v5l, &v5l->ts[16], true);
|
||||
}
|
||||
|
||||
struct v5x_interface *v5x_interface_alloc(struct v5x_instance *v5i, enum v5x_dialect dialect,
|
||||
uint32_t id, uint8_t variant, int (*ph_data_req_cb)(struct msgb *msg, void *cbdata))
|
||||
{
|
||||
struct v5x_interface *v5if = talloc_zero(v5i, struct v5x_interface);
|
||||
//struct v5x_link *v5l;
|
||||
struct v5x_c_channel *primary_c_chan;
|
||||
|
||||
v5if->instance = v5i;
|
||||
v5if->dialect = dialect;
|
||||
v5if->id = id;
|
||||
v5if->variant = variant;
|
||||
|
||||
/* primary link must alwasy be present */
|
||||
v5x_link_init(&v5if->links[0], v5if, 0);
|
||||
v5if->primary_link = &v5if->links[0];
|
||||
|
||||
primary_c_chan = &v5if->primary_link->c_channel[0];
|
||||
|
||||
v5if->control.ctrl = v51_ctrl_create(v5if, NULL, 0);
|
||||
if (!v5if->control.ctrl)
|
||||
return NULL;
|
||||
v5if->control.li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if, &lapd_profile_lapv5dl, "control");
|
||||
v5if->control.c_chan = primary_c_chan;
|
||||
|
||||
v5if->pstn.li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if, &lapd_profile_lapv5dl, "pstn");
|
||||
v5if->pstn.c_chan = primary_c_chan;
|
||||
|
||||
if (v5if->dialect == V5X_DIALECT_V52) {
|
||||
v5if->lcp.li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if, &lapd_profile_lapv5dl, "lcp");
|
||||
v5if->lcp.c_chan = primary_c_chan;
|
||||
|
||||
v5if->bcc.li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if, &lapd_profile_lapv5dl, "bcc");
|
||||
v5if->bcc.c_chan = primary_c_chan;
|
||||
|
||||
v5if->protection[0].li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if, &lapd_profile_lapv5dl, "protection0");
|
||||
v5if->protection[0].c_chan = primary_c_chan;
|
||||
|
||||
//protection[1] ?
|
||||
}
|
||||
|
||||
INIT_LLIST_HEAD(&v5if->user_ports);
|
||||
|
||||
llist_add_tail(&v5if->list, &v5i->interfaces);
|
||||
|
||||
return v5if;
|
||||
}
|
||||
|
||||
#warning hacking
|
||||
int ph_data_req_raw(struct msgb *msg, int ts);
|
||||
void ph_socket_rx_cb(ph_socket_t *s, int channel, uint8_t prim, uint8_t *data, int length)
|
||||
{
|
||||
struct v5x_user_port *v5up = s->priv;
|
||||
|
||||
switch (prim) {
|
||||
case PH_PRIM_CTRL_REQ:
|
||||
/* deactivate channels, if active */
|
||||
if ((channel == 0 || channel == 3) && length && *data == PH_CTRL_BLOCK) {
|
||||
v5up->ts_activated[0] = 0;
|
||||
v5up->ts_activated[1] = 0;
|
||||
}
|
||||
v5x_mph_rcv_le(v5up, prim, data, length);
|
||||
break;
|
||||
case PH_PRIM_ACT_REQ:
|
||||
if (channel == 1 || channel == 2) {
|
||||
v5up->ts_activated[channel - 1] = 1;
|
||||
ph_socket_tx_msg(s, channel, PH_PRIM_ACT_IND, NULL, 0);
|
||||
break;
|
||||
}
|
||||
v5x_mph_rcv_le(v5up, prim, data, length);
|
||||
break;
|
||||
case PH_PRIM_DACT_REQ:
|
||||
if (channel == 1 || channel == 2) {
|
||||
v5up->ts_activated[channel - 1] = 0;
|
||||
ph_socket_tx_msg(s, channel, PH_PRIM_DACT_IND, NULL, 0);
|
||||
break;
|
||||
}
|
||||
v5x_mph_rcv_le(v5up, prim, data, length);
|
||||
break;
|
||||
case PH_PRIM_DATA_REQ:
|
||||
if (v5up->type == V5X_USER_TYPE_PSTN && channel == 0) {
|
||||
struct msgb *msg = msgb_alloc_headroom(length + 32, 32, "V5 PSTN MSG");
|
||||
memcpy(msgb_put(msg, length), data, length);
|
||||
v5x_fe_rcv_le(v5up, msg);
|
||||
} else if (v5up->type == V5X_USER_TYPE_ISDN && channel == 3) {
|
||||
struct msgb *msg = msgb_alloc_headroom(length + 32, 32, "V5 EF MSG");
|
||||
memcpy(msgb_put(msg, length), data, length);
|
||||
lapv5ef_tx(v5up, msg);
|
||||
} else if ((channel == 1 || channel == 2) && v5up->ts_nr[channel - 1]) {
|
||||
struct msgb *msg = msgb_alloc_headroom(length + 32, 32, "B MSG");
|
||||
memcpy(msgb_put(msg, length), data, length);
|
||||
ph_data_req_raw(msg, v5up->ts_nr[channel - 1]);
|
||||
}
|
||||
/* always confirm */
|
||||
ph_socket_tx_msg(s, channel, PH_PRIM_DATA_CNF, NULL, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
struct v5x_user_port *v5x_user_port_create(struct v5x_interface *v5if, uint16_t nr, enum v5x_user_type type, uint8_t ts1, uint8_t ts2)
|
||||
{
|
||||
struct v5x_user_port *v5up;
|
||||
int rc;
|
||||
|
||||
v5up = talloc_zero(v5if, struct v5x_user_port);
|
||||
if (!v5up)
|
||||
return NULL;
|
||||
|
||||
llist_add_tail(&v5up->list, &v5if->user_ports);
|
||||
|
||||
v5up->inst = v5if;
|
||||
v5up->nr = nr;
|
||||
v5up->type = type;
|
||||
|
||||
/* assign ports */
|
||||
if (v5if->dialect == V5X_DIALECT_V51) {
|
||||
OSMO_ASSERT(ts1 >= 1 && ts1 <= 31);
|
||||
v5if->links[0].ts[ts1].v5up = v5up;
|
||||
v5up->ts_nr[0] = ts1;
|
||||
if (type == V5X_USER_TYPE_ISDN) {
|
||||
OSMO_ASSERT(ts2 >= 1 && ts2 <= 31);
|
||||
v5if->links[0].ts[ts2].v5up = v5up;
|
||||
v5up->ts_nr[1] = ts2;
|
||||
}
|
||||
}
|
||||
|
||||
v5up->ctrl = v51_ctrl_create(NULL, v5up, nr);
|
||||
if (!v5up->ctrl) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create control protocol\n");
|
||||
v5x_user_port_destroy(v5up);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case V5X_USER_TYPE_ISDN:
|
||||
sprintf(v5up->ifname, "isdn-%d", nr);
|
||||
OSMO_ASSERT(nr <= 8175);
|
||||
v5up->port_fi = v51_ctrl_le_i_port_create(v5up, nr);
|
||||
if (!v5up->port_fi) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create port control\n");
|
||||
v5x_user_port_destroy(v5up);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case V5X_USER_TYPE_PSTN:
|
||||
sprintf(v5up->ifname, "pstn-%d", nr);
|
||||
OSMO_ASSERT(nr <= 32767);
|
||||
v5up->port_fi = v51_ctrl_le_p_port_create(v5up, nr);
|
||||
if (!v5up->port_fi) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create port control\n");
|
||||
v5x_user_port_destroy(v5up);
|
||||
return NULL;
|
||||
}
|
||||
v5up->pstn.proto = v5x_pstn_create(v5up, nr);
|
||||
if (!v5up->pstn.proto) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create PSTN protocol\n");
|
||||
v5x_user_port_destroy(v5up);
|
||||
return NULL;
|
||||
}
|
||||
/* bring port into service */
|
||||
v5x_pstn_mdu_snd(v5up, MDU_CTRL_port_blocked);
|
||||
break;
|
||||
}
|
||||
|
||||
rc = ph_socket_init(&v5up->ph_socket, ph_socket_rx_cb, v5up, v5up->ifname, 1);
|
||||
if (rc < 0) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create PH-socket\n");
|
||||
v5x_user_port_destroy(v5up);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return v5up;
|
||||
}
|
||||
|
||||
void v5x_user_port_destroy(struct v5x_user_port *v5up)
|
||||
{
|
||||
struct v5x_interface *v5if = v5up->inst;
|
||||
|
||||
/* unassign ports */
|
||||
if (v5up->ts_nr[0])
|
||||
v5if->links[v5up->link_nr[0]].ts[v5up->ts_nr[0]].v5up = NULL;
|
||||
if (v5up->ts_nr[1])
|
||||
v5if->links[v5up->link_nr[1]].ts[v5up->ts_nr[1]].v5up = NULL;
|
||||
|
||||
if (v5up->ctrl)
|
||||
v51_ctrl_destroy(v5up->ctrl);
|
||||
|
||||
if (v5up->port_fi)
|
||||
osmo_fsm_inst_free(v5up->port_fi);
|
||||
|
||||
if (v5up->pstn.proto)
|
||||
v5x_pstn_destroy(v5up->pstn.proto);
|
||||
|
||||
ph_socket_exit(&v5up->ph_socket);
|
||||
|
||||
llist_del(&v5up->list);
|
||||
|
||||
free(v5up);
|
||||
}
|
||||
|
||||
struct v5x_user_port *v5x_user_port_find(struct v5x_interface *v5if, uint16_t nr, bool is_isdn)
|
||||
{
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
llist_for_each_entry(v5up, &v5if->user_ports, list) {
|
||||
if (v5up->nr == nr && is_isdn == (v5up->type == V5X_USER_TYPE_ISDN))
|
||||
return v5up;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#include <osmocom/vty/command.h>
|
||||
|
||||
enum v5_vty_node {
|
||||
INTERFACE_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
};
|
||||
|
||||
struct cmd_node interface_node = {
|
||||
INTERFACE_NODE,
|
||||
"%s(config-if)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
DEFUN(cfg_interface, cfg_interface_cmd,
|
||||
"interface", "Configure the V5 interface")
|
||||
{
|
||||
struct v5x_instance *v5i = (struct v5x_instance *)v5_instances.next;
|
||||
struct v5x_interface *v5if = (struct v5x_interface *)v5i->interfaces.next;
|
||||
|
||||
vty->node = INTERFACE_NODE;
|
||||
vty->index = v5if;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_port_pstn, cfg_port_pstn_cmd,
|
||||
"port pstn <0-32767> <1-31>",
|
||||
"Create V5 user port\n" "PSTN user port\n" "L3 address\n" "Time slot")
|
||||
{
|
||||
struct v5x_interface *v5if = vty->index;
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
v5up = v5x_user_port_find(v5if, atoi(argv[0]), false);
|
||||
if (v5up) {
|
||||
vty_out(vty, "%%Given PSTN user port already exists, remove first.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
v5up = v5x_user_port_create(v5if, atoi(argv[0]), V5X_USER_TYPE_PSTN, atoi(argv[1]), 0);
|
||||
if (!v5up) {
|
||||
vty_out(vty, "%%Failed to create PSTN user port.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_no_port_pstn, cfg_no_port_pstn_cmd,
|
||||
"no port pstn <0-32767>",
|
||||
NO_STR "Delete V5 user port\n" "PSTN user port\n" "L3 address\n")
|
||||
{
|
||||
struct v5x_interface *v5if = vty->index;
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
v5up = v5x_user_port_find(v5if, atoi(argv[0]), false);
|
||||
if (!v5up) {
|
||||
vty_out(vty, "%%Given PSTN user port does not exist.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
//FIXME: delete
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_port_isdn, cfg_port_isdn_cmd,
|
||||
"port isdn <0-8175> <1-31> <1-31>",
|
||||
"Create V5 user port\n" "ISDN user port\n" "L3 address\n" "Time slot 1\n" "Time slot 2\n")
|
||||
{
|
||||
struct v5x_interface *v5if = vty->index;
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
v5up = v5x_user_port_find(v5if, atoi(argv[0]), true);
|
||||
if (v5up) {
|
||||
vty_out(vty, "%%Given ISDN user port already exists, remove first.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
v5up = v5x_user_port_create(v5if, atoi(argv[0]), V5X_USER_TYPE_ISDN, atoi(argv[1]), atoi(argv[2]));
|
||||
if (!v5up) {
|
||||
vty_out(vty, "%%Failed to create ISDN user port.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_no_port_isdn, cfg_no_port_isdn_cmd,
|
||||
"no port isdn <0-8175>",
|
||||
NO_STR "Delete V5 user port\n" "ISDN user port\n" "L3 address\n")
|
||||
{
|
||||
struct v5x_interface *v5if = vty->index;
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
v5up = v5x_user_port_find(v5if, atoi(argv[0]), true);
|
||||
if (!v5up) {
|
||||
vty_out(vty, "%%Given ISDN user port does not exist.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
//FIXME: delete
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static int config_write_interface(struct vty *vty)
|
||||
{
|
||||
//FIXME
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int v5x_vty_init(void)
|
||||
{
|
||||
install_element(CONFIG_NODE, &cfg_interface_cmd);
|
||||
install_node(&interface_node, config_write_interface);
|
||||
install_element(INTERFACE_NODE, &cfg_port_pstn_cmd);
|
||||
install_element(INTERFACE_NODE, &cfg_no_port_pstn_cmd);
|
||||
install_element(INTERFACE_NODE, &cfg_port_isdn_cmd);
|
||||
install_element(INTERFACE_NODE, &cfg_no_port_isdn_cmd);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue