DAHDI trunkdev support

DAHDI trunkdev is a newly-introduced 'virtual trunk' character device
which is used instead of a real hardware driver. This means that an
application (such as osmo-e1d) can implement a virtual E1 trunk and
receive and transmit E1 frame data which is exposed to DAHDI users
just like the data from a real physical E1 span.

In order to build DAHDI trunkdev support into osmo-e1d, you will need
a special fork of dahdi containing the required support, currently
the laforge/trunkdev branch of the following repository:
	https://gitea.osmocom.org/retronetworking/dahdi-linux

Change-Id: Ib15a7313fcd63e1ed9f2f5b349df967bc4335ec2
This commit is contained in:
Harald Welte 2022-11-10 01:19:35 +01:00
parent 632517011d
commit 8aba31092f
15 changed files with 490 additions and 15 deletions

View File

@ -75,6 +75,21 @@ then
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
fi
AC_ARG_ENABLE(dahdi-trunkdev,
[AS_HELP_STRING(
[--enable-dahdi-trunkdev],
[Enable support for DAHDI trunkdev [default=no]],
)],
[ENABLE_DAHDI_TRUNKDEV=$enableval], [ENABLE_DAHDI_TRUNKDEV="no"])
if test x"$ENABLE_DAHDI_TRUNKDEV" = x"yes"
then
AC_CHECK_TYPE(struct dahdi_trunkdev_open,
[AC_DEFINE(HAVE_DAHDI_TRUNKDEV)],
[AC_MSG_ERROR("Cannot build requested trunkdev support without matching DAHDI headers")],
[[#include <dahdi/user.h>]])
fi
AM_CONDITIONAL(ENABLE_DAHDI_TRUNKDEV, test "x$ENABLE_DAHDI_TRUNKDEV" = "xyes")
AC_SUBST(ENABLE_DAHDI_TRUNKDEV)
# Generate manuals
AC_ARG_ENABLE(manuals,

View File

@ -11,7 +11,7 @@ enum octoi_account_mode {
ACCOUNT_MODE_NONE,
ACCOUNT_MODE_ICE1USB,
ACCOUNT_MODE_REDIRECT,
ACCOUNT_MODE_DAHDI,
ACCOUNT_MODE_DAHDI_TRUNKDEV,
};
extern const struct value_string octoi_account_mode_name[];
@ -32,8 +32,9 @@ struct octoi_account {
struct osmo_sockaddr_str to; /* remote IP/port to which to redirect */
} redirect;
struct {
/* TBD */
} dahdi;
char *name; /* DAHDI trunkdev name */
uint8_t line_nr; /* line number inside icE1usb */
} dahdi_trunkdev;
} u;
};
@ -71,6 +72,8 @@ struct octoi_ops {
struct octoi_account *acc);
/* OCTOI library notifies the application that a given peer has disconnected */
void (*peer_disconnected)(struct octoi_peer *peer);
/* OCTOI library notifies the application that a given client has been updated */
void (*client_updated)(struct octoi_client *client);
};
struct octoi_daemon {

View File

@ -76,3 +76,8 @@ osmo_e1gen_SOURCES = \
usb.c \
vty.c \
$(NULL)
if ENABLE_DAHDI_TRUNKDEV
osmo_e1d_SOURCES += dahdi_trunkdev.c
osmo_e1gen_SOURCES += dahdi_trunkdev.c
endif

233
src/dahdi_trunkdev.c Normal file
View File

@ -0,0 +1,233 @@
/*
* trunkdev.c
*
* (C) 2022 by Harald Welte <laforge@osmocom.org>
*
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*/
#include <errno.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <talloc.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <osmocom/core/utils.h>
#include <dahdi/user.h>
#include "e1d.h"
#include "log.h"
/***********************************************************************
* low-level trunkdev routines
***********************************************************************/
static int trunkdev_specify(int fd, const char *name)
{
struct dahdi_trunkdev_open td_o = { 0 };
OSMO_STRLCPY_ARRAY(td_o.name, name);
return ioctl(fd, DAHDI_TRUNKDEV_OPEN, &td_o);
}
/***********************************************************************
* osmo-e1d interface
***********************************************************************/
/* default dahdi chunk size: 8 chunks (in this case E1 frames) per read/write */
#define DAHDI_CHUNKSIZE 8
#define BYTES_PER_FRAME 32
/* one E1 line (DAHDI span) inside the trunkdev */
struct e1_trunkdev_line_data {
unsigned int basechan; /* so far, only 0 supported */
unsigned int numchans; /* so far, onlt 32 supported */
};
/* one DAHDI trunkdev */
struct e1_trunkdev_intf_data {
/* file descriptor to the character device /dev/dahdi/trunkdev */
struct osmo_fd ofd;
};
/* file-descriptor call-back. Triggered by DAHDI via poll(), whenever
* there is new E1 frame data available to read from trunkdev. The flow
* control in transmit side is simple: We write as * many frames as we are reading */
static int dahdi_trunkdev_fd_cb(struct osmo_fd *ofd, unsigned int what)
{
struct e1_intf *e1i = ofd->data;
struct e1_line *e1l = e1_intf_find_line(e1i, 0);
uint8_t buf[DAHDI_CHUNKSIZE*BYTES_PER_FRAME];
int rc, len;
OSMO_ASSERT(what & OSMO_FD_READ);
len = read(ofd->fd, buf, sizeof(buf));
if (len <= 0) {
LOGPIF(e1i, DTRUNKDEV, LOGL_ERROR, "Error %d during trunkdev read: %s\n", len,
strerror(errno));
return len;
} else if (len < (int) sizeof(buf)) {
/* for some not yet known reason this happens quite often, typically 244 of 256 bytes,
* followed by the remaining 32 bytes in the next read. No data is lost, it just
* costs a lot of extra syscalls / context switches */
LOGPIF(e1i, DTRUNKDEV, LOGL_DEBUG, "Short read during trunkdev read: %d < %zu\n",
len, sizeof(buf));
}
if (len % BYTES_PER_FRAME) {
LOGPIF(e1i, DTRUNKDEV, LOGL_ERROR, "Odd number of bytes during read: %d\n", len);
return -EIO;
}
if (!e1l) {
/* no line: discard input; transmit all-ff (BLUE) */
memset(buf, 0xff, len);
} else {
/* DAHDI trunkdev currently only supports one span/line per trunk */
rc = e1_line_demux_in(e1l, buf, len, -1);
#if 0
if (rc < 0) {
LOGPLI(e1l, DTRUNKDEV, LOGL_ERROR, "Error %d during e1_line_demux_in()\n", rc);
return rc;
}
#endif
/* only pull as many frames out of our muxer as we have just read from the trunk */
len = e1_line_mux_out(e1l, buf, len/BYTES_PER_FRAME);
if (len < 0) {
LOGPLI(e1l, DTRUNKDEV, LOGL_ERROR, "Error %d during mux_out\n", len);
return len;
}
}
rc = write(ofd->fd, buf, len);
if (rc <= 0) {
LOGPIF(e1i, DTRUNKDEV, LOGL_ERROR, "Error %d during trunkdev write: %s\n", rc,
strerror(errno));
return rc;
} else if (rc < len) {
LOGPIF(e1i, DTRUNKDEV, LOGL_ERROR, "Short write during trunkdev write: %d < %d\n",
rc, len);
}
return 0;
}
int
e1_dahdi_trunkdev_open(struct e1_intf *e1i)
{
struct dahdi_trunkdev_create _cr;
struct e1_trunkdev_intf_data *tid;
struct e1_line *e1l;
int rc, fd;
/* various sanity checks */
if (e1i->drv != E1_DRIVER_DAHDI_TRUNKDEV) {
LOGPIF(e1i, DTRUNKDEV, LOGL_ERROR, "Cannot open non-trunkdev trunk as trunkdev\n");
return -EINVAL;
}
if (!e1i->dahdi_trunkdev.name) {
LOGPIF(e1i, DTRUNKDEV, LOGL_ERROR, "Cannot open trunkdev without name\n");
return -EINVAL;
}
if (strlen(e1i->dahdi_trunkdev.name) > sizeof(_cr.name)-1) {
LOGPIF(e1i, DTRUNKDEV, LOGL_ERROR, "Cannot open trunkdev with excessively long name\n");
return -EINVAL;
}
if (e1i->drv_data) {
LOGPIF(e1i, DTRUNKDEV, LOGL_NOTICE, "Cannot open trunkdev that's already open\n");
return -EBUSY;
}
/* open the trunkdev */
fd = open("/dev/dahdi/trunkdev", O_RDWR);
if (fd < 0) {
LOGPIF(e1i, DTRUNKDEV, LOGL_ERROR, "Cannot open /dev/dahdi/trunkdev: %s\n",
strerror(errno));
return -errno;
}
/* try to select the trunk by name */
rc = trunkdev_specify(fd, e1i->dahdi_trunkdev.name);
if (rc < 0) {
LOGPIF(e1i, DTRUNKDEV, LOGL_ERROR, "Unable to specify trunkdev '%s': %s\n",
e1i->dahdi_trunkdev.name, strerror(errno));
/* TODO: auto- create on demand? */
close(fd);
return -errno;
}
LOGPIF(e1i, DTRUNKDEV, LOGL_NOTICE, "Successfully opened trunkdev '%s'\n", e1i->dahdi_trunkdev.name);
tid = talloc_zero(e1i->e1d->ctx, struct e1_trunkdev_intf_data);
OSMO_ASSERT(tid);
osmo_fd_setup(&tid->ofd, fd, OSMO_FD_READ, dahdi_trunkdev_fd_cb, e1i, e1i->id);
osmo_fd_register(&tid->ofd);
e1i->drv_data = tid;
/* ensure line0 exists */
if (!e1_intf_find_line(e1i, 0)) {
e1l = e1_line_new(e1i, 0, NULL);
e1l->mode = E1_LINE_MODE_E1OIP;
}
/* activate line */
llist_for_each_entry(e1l, &e1i->lines, list)
e1_line_active(e1l);
return 0;
}
int
e1_dahdi_trunkdev_close(struct e1_intf *e1i)
{
struct e1_trunkdev_intf_data *tid = e1i->drv_data;
int rc;
if (e1i->drv != E1_DRIVER_DAHDI_TRUNKDEV) {
LOGPIF(e1i, DTRUNKDEV, LOGL_ERROR, "Cannot close non-trunkdev trunk as trunkdev\n");
return -EINVAL;
}
if (!tid) {
LOGPIF(e1i, DTRUNKDEV, LOGL_DEBUG, "No need to close trunkdev; was not open\n");
return 0;
}
osmo_fd_unregister(&tid->ofd);
/* we're not deleting the dahdi trunkdev as that might upset the applications using
* the channel-side of it */
rc = close(tid->ofd.fd);
if (rc < 0)
LOGPIF(e1i, DTRUNKDEV, LOGL_ERROR, "Error closing trunkdev: %s\n", strerror(errno));
talloc_free(tid);
e1i->drv_data = tid = NULL;
LOGPIF(e1i, DTRUNKDEV, LOGL_NOTICE, "Closed trunkdev '%s'\n", e1i->dahdi_trunkdev.name);
return 0;
}

View File

@ -161,6 +161,7 @@ struct e1_line {
enum e1_driver {
E1_DRIVER_USB,
E1_DRIVER_VPAIR,
E1_DRIVER_DAHDI_TRUNKDEV,
};
extern const struct value_string e1_driver_names[];
@ -180,6 +181,9 @@ struct e1_intf {
uint16_t fine;
} gpsdo;
} usb;
struct {
char *name;
} dahdi_trunkdev;
bool vty_created;
enum e1_driver drv;
@ -210,6 +214,9 @@ e1d_find_intf(struct e1_daemon *e1d, uint8_t id);
struct e1_intf *
e1d_find_intf_by_usb_serial(struct e1_daemon *e1d, const char *serial_str);
struct e1_intf *
e1d_find_intf_by_trunkdev_name(struct e1_daemon *e1d, const char *name);
void
e1_intf_destroy(struct e1_intf *intf);
@ -244,6 +251,14 @@ e1d_vpair_create(struct e1_daemon *e1d, unsigned int num_lines);
struct e1_intf *
e1d_vpair_intf_peer(struct e1_intf *intf);
#ifdef HAVE_DAHDI_TRUNKDEV
int
e1_dahdi_trunkdev_open(struct e1_intf *e1i);
int
e1_dahdi_trunkdev_close(struct e1_intf *e1i);
#endif
int
e1oip_line_demux_in(struct e1_line *line, const uint8_t *buf, int ftr);

View File

@ -48,6 +48,16 @@ find_line_by_usb_serial(struct e1_daemon *e1d, const char *serial_str, uint8_t i
return e1_intf_find_line(e1i, id);
}
/* convenience helper function finding a e1_line for given name + id */
static struct e1_line *
find_line_by_trunkdev_name(struct e1_daemon *e1d, const char *name, uint8_t id)
{
struct e1_intf *e1i = e1d_find_intf_by_trunkdev_name(e1d, name);
if (!e1i)
return NULL;
return e1_intf_find_line(e1i, id);
}
static struct e1_line *
find_line_for_account(struct e1_daemon *e1d, const struct octoi_account *acc)
{
@ -55,8 +65,9 @@ find_line_for_account(struct e1_daemon *e1d, const struct octoi_account *acc)
case ACCOUNT_MODE_ICE1USB:
return find_line_by_usb_serial(e1d, acc->u.ice1usb.usb_serial,
acc->u.ice1usb.line_nr);
case ACCOUNT_MODE_DAHDI:
OSMO_ASSERT(0); /* TODO */
case ACCOUNT_MODE_DAHDI_TRUNKDEV:
return find_line_by_trunkdev_name(e1d, acc->u.dahdi_trunkdev.name,
acc->u.dahdi_trunkdev.line_nr);
break;
default:
return NULL;
@ -127,6 +138,35 @@ _e1d_octoi_client_connected_cb(struct octoi_server *srv, struct octoi_peer *peer
return line;
}
static void
_e1d_octoi_client_updated_cb(struct octoi_client *clnt)
{
struct e1_daemon *e1d = g_octoi->priv;
struct e1_line *line;
/* find line for client */
line = find_line_for_account(e1d, clnt->cfg.account);
if (!line)
return;
if (line->mode != E1_LINE_MODE_E1OIP)
return;
/* check if line is active */
if (!osmo_timer_pending(&line->ts0.timer))
return;
/* TODO: kill old peer, if != current peer */
if (!line->octoi_peer)
line->octoi_peer = octoi_client_get_peer(clnt);
else
OSMO_ASSERT(line->octoi_peer == octoi_client_get_peer(clnt));
/* start client for peer (if not started) */
OSMO_ASSERT(line->octoi_peer);
octoi_clnt_start_for_peer(line->octoi_peer, clnt->cfg.account);
}
/* OCTOI has detected that a given peer has vanished; delete reference to it */
static void
_e1d_octoi_peer_disconnected_cb(struct octoi_peer *peer)
@ -148,5 +188,6 @@ _e1d_octoi_peer_disconnected_cb(struct octoi_peer *peer)
const struct octoi_ops e1d_octoi_ops = {
.client_connected = &_e1d_octoi_client_connected_cb,
.client_updated = &_e1d_octoi_client_updated_cb,
.peer_disconnected = &_e1d_octoi_peer_disconnected_cb,
};

View File

@ -46,6 +46,7 @@
const struct value_string e1_driver_names[] = {
{ E1_DRIVER_USB, "usb" },
{ E1_DRIVER_VPAIR, "vpair" },
{ E1_DRIVER_DAHDI_TRUNKDEV, "dahdi-trunkdev" },
{ 0, NULL }
};
@ -133,6 +134,22 @@ e1d_find_intf_by_usb_serial(struct e1_daemon *e1d, const char *serial_str)
return NULL;
}
struct e1_intf *
e1d_find_intf_by_trunkdev_name(struct e1_daemon *e1d, const char *name)
{
struct e1_intf *intf;
if (!name)
return NULL;
llist_for_each_entry(intf, &e1d->interfaces, list) {
if (intf->dahdi_trunkdev.name && !strcmp(intf->dahdi_trunkdev.name, name))
return intf;
}
return NULL;
}
struct e1_line *
e1_intf_find_line(struct e1_intf *intf, uint8_t id)
{
@ -300,6 +317,11 @@ static struct octoi_client *octoi_client_by_line(struct e1_line *line)
line->id == acc->u.ice1usb.line_nr)
return clnt;
break;
case ACCOUNT_MODE_DAHDI_TRUNKDEV:
if (!strcmp(line->intf->dahdi_trunkdev.name, acc->u.dahdi_trunkdev.name) &&
line->id == acc->u.dahdi_trunkdev.line_nr)
return clnt;
break;
case ACCOUNT_MODE_NONE:
case ACCOUNT_MODE_REDIRECT:
break;

View File

@ -38,6 +38,12 @@ static const struct log_info_cat default_categories[] = {
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
[DTRUNKDEV] = {
.name = "DTRUNKDEV",
.description = "DAHDI trunkdev driver",
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
};
const struct log_info log_info = {

View File

@ -29,6 +29,7 @@
enum {
DE1D,
DXFR,
DTRUNKDEV,
};
#define LOGPIF(itf, ss, lvl, fmt, args...) \

View File

@ -43,6 +43,7 @@ static struct octoi_client *client4account(struct octoi_account *acc)
int octoi_vty_go_parent(struct vty *vty)
{
struct octoi_account *acc;
struct octoi_client *clnt;
switch (vty->node) {
case OCTOI_ACCOUNT_NODE:
@ -54,6 +55,14 @@ int octoi_vty_go_parent(struct vty *vty)
vty->node = OCTOI_CLNT_NODE;
vty->index = client4account(acc);
break;
case OCTOI_CLNT_NODE:
clnt = vty->index;
/* check if we have a (new?) line for this client */
if (g_octoi->ops->client_updated)
g_octoi->ops->client_updated(clnt);
vty->node = CONFIG_NODE;
vty->index = NULL;
break;
default:
vty->node = CONFIG_NODE;
vty->index = NULL;

View File

@ -281,6 +281,10 @@ void octoi_client_vty_init(void)
install_element(OCTOI_CLNT_ACCOUNT_NODE, &cfg_account_mode_cmd);
install_element(OCTOI_CLNT_ACCOUNT_NODE, &cfg_account_batching_factor_cmd);
install_element(OCTOI_CLNT_ACCOUNT_NODE, &cfg_account_prefill_frame_count_cmd);
#ifdef HAVE_DAHDI_TRUNKDEV
install_element(OCTOI_CLNT_ACCOUNT_NODE, &cfg_account_trunkdev_name_cmd);
install_element(OCTOI_CLNT_ACCOUNT_NODE, &cfg_account_trunkdev_line_cmd);
#endif /* HAVE_DAHDI_TRUNKDEV */
install_node(&clnt_node, config_write_octoi_clnt);
install_element(CONFIG_NODE, &cfg_client_cmd);

View File

@ -109,7 +109,7 @@ static void srv_st_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
switch (acc->mode) {
case ACCOUNT_MODE_ICE1USB:
case ACCOUNT_MODE_DAHDI:
case ACCOUNT_MODE_DAHDI_TRUNKDEV:
/* check if a matching device exists for that account */
st->app_priv = g_octoi->ops->client_connected(srv, st->peer, acc);
if (!st->app_priv) {

View File

@ -48,7 +48,7 @@ const struct value_string octoi_account_mode_name[] = {
{ ACCOUNT_MODE_NONE, "none" },
{ ACCOUNT_MODE_ICE1USB, "ice1usb" },
{ ACCOUNT_MODE_REDIRECT, "redirect" },
{ ACCOUNT_MODE_DAHDI, "dahdi" },
{ ACCOUNT_MODE_DAHDI_TRUNKDEV, "dahdi-trunkdev" },
{ 0, NULL }
};
@ -288,10 +288,11 @@ DEFUN(cfg_srv_no_account, cfg_serv_no_account_cmd,
#endif
gDEFUN(cfg_account_mode, cfg_account_mode_cmd,
"mode (ice1usb|redirect)",
"mode (ice1usb|redirect|dahdi-trunkdev)",
"Operational mode of account\n"
"Connect to local icE1usb (identified by USB serial + line number)\n"
"Redirect to other IP/Port\n")
"Redirect to other IP/Port\n"
"Use DAHDI trunkdev virtual trunk\n")
{
struct octoi_account *acc = vty->index;
@ -309,6 +310,14 @@ gDEFUN(cfg_account_mode, cfg_account_mode_cmd,
acc->mode = ACCOUNT_MODE_ICE1USB;
} else if (!strcmp(argv[0], "redirect")) {
acc->mode = ACCOUNT_MODE_REDIRECT;
} else if (!strcmp(argv[0], "dahdi-trunkdev")) {
#ifdef HAVE_DAHDI_TRUNKDEV
acc->mode = ACCOUNT_MODE_DAHDI_TRUNKDEV;
#else
vty_out(vty, "%% This build wasn't compiled with dahdi-trunkdev support%s",
VTY_NEWLINE);
return CMD_WARNING;
#endif
} else
OSMO_ASSERT(0);
@ -400,6 +409,42 @@ void octoi_vty_show_one_account(struct vty *vty, const char *pfx, struct octoi_a
acc->batching_factor, acc->prefill_frame_count, VTY_NEWLINE);
}
#ifdef HAVE_DAHDI_TRUNKDEV
#define DAHDI_STR "DAHDI trunkdev settings\n"
gDEFUN(cfg_account_trunkdev_name, cfg_account_trunkdev_name_cmd,
"dahdi-trunkdev name NAME",
DAHDI_STR "Identify DAHDI trunkdev device by name\n"
"Name of the DAHDI trunkdev device\n")
{
struct octoi_account *acc = vty->index;
if (acc->mode != ACCOUNT_MODE_DAHDI_TRUNKDEV) {
vty_out(vty, "%% Error: Not in dahdi-trunkdev mode!%s", VTY_NEWLINE);
return CMD_WARNING;
}
osmo_talloc_replace_string(acc, &acc->u.dahdi_trunkdev.name, argv[0]);
return CMD_SUCCESS;
}
gDEFUN(cfg_account_trunkdev_line, cfg_account_trunkdev_line_cmd,
"dahdi-trunkdev line-number <0-1>",
DAHDI_STR "E1 Line number\n" "E1 Line number\n")
{
struct octoi_account *acc = vty->index;
if (acc->mode != ACCOUNT_MODE_DAHDI_TRUNKDEV) {
vty_out(vty, "%% Error: Not in dahdi-trunkdev mode!%s", VTY_NEWLINE);
return CMD_WARNING;
}
acc->u.dahdi_trunkdev.line_nr = atoi(argv[0]);
return CMD_SUCCESS;
}
#endif /* HAVE_DAHDI_TRUNKDEV */
void octoi_vty_write_one_account(struct vty *vty, const struct octoi_account *acc)
{
if (!acc)
@ -427,8 +472,13 @@ void octoi_vty_write_one_account(struct vty *vty, const struct octoi_account *ac
vty_out(vty, " redirect %s %u%s", acc->u.redirect.to.ip, acc->u.redirect.to.port,
VTY_NEWLINE);
break;
case ACCOUNT_MODE_DAHDI:
OSMO_ASSERT(0);
case ACCOUNT_MODE_DAHDI_TRUNKDEV:
#ifdef HAVE_DAHDI_TRUNKDEV
if (acc->u.dahdi_trunkdev.name)
vty_out(vty, " dahdi-trunkdev name %s%s", acc->u.dahdi_trunkdev.name, VTY_NEWLINE);
vty_out(vty, " dahdi-trunkdev line-number %u%s", acc->u.dahdi_trunkdev.line_nr, VTY_NEWLINE);
#endif
break;
}
}
@ -489,6 +539,10 @@ void octoi_server_vty_init(void)
install_element(OCTOI_ACCOUNT_NODE, &cfg_account_redir_cmd);
install_element(OCTOI_ACCOUNT_NODE, &cfg_account_batching_factor_cmd);
install_element(OCTOI_ACCOUNT_NODE, &cfg_account_prefill_frame_count_cmd);
#ifdef HAVE_DAHDI_TRUNKDEV
install_element(OCTOI_ACCOUNT_NODE, &cfg_account_trunkdev_name_cmd);
install_element(OCTOI_ACCOUNT_NODE, &cfg_account_trunkdev_line_cmd);
#endif /* HAVE_DAHDI_TRUNKDEV */
install_node(&srv_node, config_write_octoi_srv);
install_element(CONFIG_NODE, &cfg_server_cmd);

View File

@ -9,6 +9,8 @@ extern struct cmd_element cfg_account_ice1_serno_cmd;
extern struct cmd_element cfg_account_ice1_line_cmd;
extern struct cmd_element cfg_account_batching_factor_cmd;
extern struct cmd_element cfg_account_prefill_frame_count_cmd;
extern struct cmd_element cfg_account_trunkdev_name_cmd;
extern struct cmd_element cfg_account_trunkdev_line_cmd;
struct octoi_account *octoi_client_account_create(struct octoi_client *clnt, const char *user_id);

View File

@ -88,10 +88,19 @@ static void vty_dump_ts(struct vty *vty, const struct e1_ts *ts)
static const char *intf_serno(const struct e1_intf *intf)
{
if (intf->usb.serial_str)
return intf->usb.serial_str;
else
return "unnamed";
switch (intf->drv) {
case E1_DRIVER_USB:
if (intf->usb.serial_str)
return intf->usb.serial_str;
break;
case E1_DRIVER_DAHDI_TRUNKDEV:
if (intf->dahdi_trunkdev.name)
return intf->dahdi_trunkdev.name;
break;
default:
break;
}
return "unnamed";
}
static void vty_dump_intf(struct vty *vty, const struct e1_intf *intf)
@ -288,6 +297,31 @@ DEFUN(cfg_e1d_if_vpair, cfg_e1d_if_vpair_cmd, "interface <0-255> vpair",
return CMD_SUCCESS;
}
#ifdef HAVE_DAHDI_TRUNKDEV
DEFUN(cfg_e1d_if_trunkdev, cfg_e1d_if_trunkdev_cmd, "interface <0-255> dahdi-trunkdev",
"Configure a DAHDI trunkdev interface (virtual trunk)\n"
"E1 Interface Number\n"
"Use DAHDI trunkdev driver for this interface\n")
{
struct e1_intf *intf;
int intf_nr = atoi(argv[0]);
intf = e1d_find_intf(vty_e1d, intf_nr);
if (!intf)
intf = e1_intf_new(vty_e1d, intf_nr, NULL);
if (!intf) {
vty_out(vty, "%% Could not create interface%s", VTY_NEWLINE);
return CMD_WARNING;
}
intf->drv = E1_DRIVER_DAHDI_TRUNKDEV;
intf->vty_created = true;
vty->index = intf;
vty->node = INTF_NODE;
return CMD_SUCCESS;
}
#endif /* HAVE_DAHDI_TRUNKDEV */
DEFUN(cfg_e1d_if_usb_serial, cfg_e1d_if_usb_serial_cmd,
"usb-serial SERNO",
"Configure the USB serial number of an E1 interface device\n"
@ -334,6 +368,26 @@ DEFUN(cfg_e1d_if_no_gpsdo_manual, cfg_e1d_if_no_gpsdo_manual_cmd,
return CMD_SUCCESS;
}
#ifdef HAVE_DAHDI_TRUNKDEV
DEFUN(cfg_e1d_if_trunkdev_name, cfg_e1d_if_trunkdev_name_cmd,
"trunkdev-name SERNO",
"Configure the name of the DAHDI trunkdev device\n"
"DAHDI trunkdev name\n")
{
struct e1_intf *intf = vty->index;
if (intf->drv != E1_DRIVER_DAHDI_TRUNKDEV)
return CMD_WARNING;
osmo_talloc_replace_string(intf, &intf->dahdi_trunkdev.name, argv[0]);
e1_dahdi_trunkdev_close(intf);
e1_dahdi_trunkdev_open(intf);
return CMD_SUCCESS;
}
#endif /* HAVE_DAHDI_TRUNKDEV */
DEFUN(cfg_e1d_if_line, cfg_e1d_if_line_cmd, "line <0-255>",
"Configure an E1 line\n"
"E1 Interface Number\n")
@ -425,6 +479,11 @@ static int config_write_e1d(struct vty *vty)
case E1_DRIVER_VPAIR:
vty_out(vty, " interface %u vpair%s", intf->id, VTY_NEWLINE);
break;
case E1_DRIVER_DAHDI_TRUNKDEV:
vty_out(vty, " interface %u dahdi-trunkdev%s", intf->id, VTY_NEWLINE);
if (intf->dahdi_trunkdev.name && strlen(intf->dahdi_trunkdev.name))
vty_out(vty, " trunkdev-name %s%s", intf->dahdi_trunkdev.name, VTY_NEWLINE);
break;
default:
break;
}
@ -449,10 +508,16 @@ void e1d_vty_init(struct e1_daemon *e1d)
install_node(&intf_node, NULL);
install_element(E1D_NODE, &cfg_e1d_if_icE1usb_cmd);
install_element(E1D_NODE, &cfg_e1d_if_vpair_cmd);
#ifdef HAVE_DAHDI_TRUNKDEV
install_element(E1D_NODE, &cfg_e1d_if_trunkdev_cmd);
#endif /* HAVE_DAHDI_TRUNKDEV */
install_element(INTF_NODE, &cfg_e1d_if_line_cmd);
install_element(INTF_NODE, &cfg_e1d_if_usb_serial_cmd);
install_element(INTF_NODE, &cfg_e1d_if_gpsdo_manual_cmd);
install_element(INTF_NODE, &cfg_e1d_if_no_gpsdo_manual_cmd);
#ifdef HAVE_DAHDI_TRUNKDEV
install_element(INTF_NODE, &cfg_e1d_if_trunkdev_name_cmd);
#endif /* HAVE_DAHDI_TRUNKDEV */
install_node(&line_node, NULL);
install_element(LINE_NODE, &cfg_e1d_if_line_mode_cmd);