laforge has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/dahdi-tools/+/34013 )


Change subject: Initial support for trunkdev
......................................................................

Initial support for trunkdev

The 'trunkdev' is a virtual DAHDI device driver that exposes the
multiplexed E1 frames on a /dev/dahdi/trunkdev device.  This is useful
for all kinds of simulated setup, as well as in  the context of
using osmo-e1d for OCTOI (Osmocom TDMoIP).

In order for the dahdi_trunkdev utility to be built, you will need
to have installed dahdi header files of dahdi-linux with trunkdev
support, currently located at
https://gitea.osmocom.org/retronetworking/dahdi-linux/src/branch/laforge/trunkdev

Change-Id: Ie05100c80fd978e7ac6f5a028e0ef6c828e0bcc3
---
M Makefile.am
M configure.ac
A trunkdev/Makefile.am
A trunkdev/dahdi-sysfs.c
A trunkdev/dahdi-sysfs.h
A trunkdev/trunkdev-tool.c
6 files changed, 468 insertions(+), 1 deletion(-)



  git pull ssh://gerrit.osmocom.org:29418/dahdi-tools refs/changes/13/34013/1

diff --git a/Makefile.am b/Makefile.am
index 9dd64b2..2c42d69 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -17,7 +17,7 @@
        #-Wformat=2
 endif

-SUBDIRS        = xpp doc hotplug
+SUBDIRS        = xpp doc hotplug trunkdev

 if PPPD
 SUBDIRS        += ppp
diff --git a/configure.ac b/configure.ac
index 826a7ec..e11eec7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -182,6 +182,7 @@
 AST_C_DEFINE_CHECK([DAHDI], [DAHDI_CODE], [dahdi/user.h])
 DAHDI23_DIR="${DAHDI_DIR}"
 AST_C_DEFINE_CHECK([DAHDI23], [DAHDI_CONFIG_NTTE], [dahdi/user.h])
+AST_C_DEFINE_CHECK([TRUNKDEV], [DAHDI_TRUNKDEV_CREATE], [dahdi/user.h])
 AST_EXT_LIB_CHECK([NEWT], [newt], [newtBell], [newt.h])
 AST_EXT_LIB_CHECK([USB], [usb], [usb_init], [usb.h])
 AST_EXT_LIB_CHECK([PCAP], [pcap], [pcap_compile], [pcap.h])
@@ -193,6 +194,7 @@

 AM_CONDITIONAL([PBX_NEWT], [test "$PBX_NEWT" = "1"])
 AM_CONDITIONAL([PBX_PCAP], [test "$PBX_PCAP" = "1" -a "$DAHDI_TXMIRROR" = "1"])
+AM_CONDITIONAL([PBX_TRUNKDEV], [test "$PBX_TRUNKDEV" = "1"])

 AC_CHECK_FUNCS([semtimedop])
 AC_CHECK_FUNCS([alarm bzero gettimeofday memset pow regcomp select socket 
strcasecmp strchr strdup strerror strrchr strstr strtol strtoul])
@@ -390,6 +392,7 @@
        xpp/xtalk/Makefile
        xpp/oct612x/Makefile
        xpp/perl_modules/Makefile
+       trunkdev/Makefile
        ])
 AC_OUTPUT

diff --git a/trunkdev/Makefile.am b/trunkdev/Makefile.am
new file mode 100644
index 0000000..af230f4
--- /dev/null
+++ b/trunkdev/Makefile.am
@@ -0,0 +1,14 @@
+CFLAGS += -g -Wall -O2 $(DAHDI_INCLUDE)
+
+if PBX_TRUNKDEV
+
+sbin_PROGRAMS = \
+       dahdi_trunkdev \
+       $(NULL)
+
+dahdi_trunkdev_SOURCES = \
+       trunkdev-tool.c \
+       dahdi-sysfs.c \
+       $(NULL)
+
+endif
diff --git a/trunkdev/dahdi-sysfs.c b/trunkdev/dahdi-sysfs.c
new file mode 100644
index 0000000..63324e9
--- /dev/null
+++ b/trunkdev/dahdi-sysfs.c
@@ -0,0 +1,88 @@
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include "dahdi-sysfs.h"
+
+/* read a sysfs attribute from a file, stripping any trailing newline */
+char *sysfs_read_attr(const char *path)
+{
+       FILE *f;
+       char *buf;
+
+       f = fopen(path, "r");
+       if (!f)
+               return NULL;
+
+       buf = malloc(256);
+       if (!buf)
+               goto out_close;
+
+       if (!fgets(buf, 256, f))
+               goto out_free;
+
+       if (strlen(buf) && buf[strlen(buf)-1] == '\n')
+               buf[strlen(buf)-1] = '\0';
+
+       fclose(f);
+       printf("%s: %s\n", path, buf);
+       return buf;
+
+out_free:
+       free(buf);
+out_close:
+       fclose(f);
+       return NULL;
+}
+
+static char *sysfs_read_span_attr(unsigned int spanno, const char *name)
+{
+       char path[PATH_MAX];
+       snprintf(path, sizeof(path), "/sys/bus/dahdi_spans/devices/span-%u/%s", 
spanno, name);
+       return sysfs_read_attr(path);
+}
+
+static int sysfs_read_span_attr_int(unsigned int spanno, const char *name)
+{
+       char *attr;
+       int attr_int;
+
+       attr = sysfs_read_span_attr(spanno, name);
+       if (!attr)
+               return -1;
+
+       attr_int = atoi(attr);
+       free(attr);
+
+       return attr_int;
+}
+
+/* return number of channels in span */
+int dahdi_span_get_basechan(unsigned int spanno)
+{
+       return sysfs_read_span_attr_int(spanno, "basechan");
+}
+
+/* return number of channels in span */
+int dahdi_span_get_channels(unsigned int spanno)
+{
+       return sysfs_read_span_attr_int(spanno, "channels");
+}
+
+char *dahdi_span_get_name(unsigned int spanno)
+{
+       return sysfs_read_span_attr(spanno, "name");
+}
+
+char *dahdi_span_get_desc(unsigned int spanno)
+{
+       return sysfs_read_span_attr(spanno, "desc");
+}
+
+char *dahdi_span_get_spantype(unsigned int spanno)
+{
+       return sysfs_read_span_attr(spanno, "spantype");
+}
diff --git a/trunkdev/dahdi-sysfs.h b/trunkdev/dahdi-sysfs.h
new file mode 100644
index 0000000..2a8eaf7
--- /dev/null
+++ b/trunkdev/dahdi-sysfs.h
@@ -0,0 +1,9 @@
+#pragma once
+
+char *sysfs_read_attr(const char *path);
+
+int dahdi_span_get_basechan(unsigned int spanno);
+int dahdi_span_get_channels(unsigned int spanno);
+char *dahdi_span_get_name(unsigned int spanno);
+char *dahdi_span_get_desc(unsigned int spanno);
+char *dahdi_span_get_spantype(unsigned int spanno);
diff --git a/trunkdev/trunkdev-tool.c b/trunkdev/trunkdev-tool.c
new file mode 100644
index 0000000..355c5d0
--- /dev/null
+++ b/trunkdev/trunkdev-tool.c
@@ -0,0 +1,334 @@
+/*
+ * trunkdev configuration program for DAHDI Telephony Interface
+ *
+ * Copyright (C) 2022 Harald Welte <[email protected]>
+ * Copyright (C) 2001-2008 Digium, Inc.
+ *
+ * All rights reserved.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2 as published by the
+ * Free Software Foundation. See the LICENSE file included with
+ * this program for more details.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include <sys/socket.h>
+#include <dahdi/user.h>
+
+#include "dahdi-sysfs.h"
+
+static int g_fd = -1;
+
+
+/* wrapper around DAHDI_TRUNKDEV_CREATE */
+static int trunkdev_create(int fd, const char *name)
+{
+       struct dahdi_trunkdev_create td_c = { 0 };
+       int rc;
+
+       strncpy(td_c.name, name, sizeof(td_c.name));
+       td_c.name[sizeof(td_c.name)-1] = 0;
+
+       rc = ioctl(fd, DAHDI_TRUNKDEV_CREATE, &td_c);
+       if (rc)
+               fprintf(stderr, "Error creating trunkdev '%s': %s\n", name, 
strerror(errno));
+
+       return rc;
+}
+
+/* wrapper around DAHDI_TRUNKDEV_DELETE */
+static int trunkdev_delete(int fd, const char *name)
+{
+       struct dahdi_trunkdev_delete td_d = { 0 };
+       int rc;
+
+       strncpy(td_d.name, name, sizeof(td_d.name));
+       td_d.name[sizeof(td_d.name)-1] = 0;
+
+       rc = ioctl(fd, DAHDI_TRUNKDEV_DELETE, &td_d);
+       if (rc)
+               fprintf(stderr, "Error deleting trunkdev '%s': %s\n", name, 
strerror(errno));
+
+       return rc;
+}
+
+/* wrapper around DAHDI_TRUNKDEV_SPECIFY */
+static int trunkdev_specify(int fd, const char *name)
+{
+       struct dahdi_trunkdev_open td_o = { 0 };
+       int rc;
+
+       strncpy(td_o.name, name, sizeof(td_o.name));
+       td_o.name[sizeof(td_o.name)-1] = 0;
+
+       rc = ioctl(fd, DAHDI_TRUNKDEV_OPEN, &td_o);
+       if (rc < 0) {
+               fprintf(stderr, "Error opening trunkdev '%s': %s\n", name, 
strerror(errno));
+               return rc;
+       }
+       return td_o.spanno;
+}
+
+/* wrapper around DAHDI_SPANCONFIG (assuming E1 with CRC4) */
+static int spanconfig(int fd, int spanno)
+{
+       struct dahdi_lineconfig dlc = {
+               .span = spanno,
+               .lbo = 0,
+               .lineconfig = DAHDI_CONFIG_CRC4,
+               .sync = 0,
+       };
+
+       return ioctl(fd, DAHDI_SPANCONFIG, &dlc);
+}
+
+/* wrapper around DAHDI_CHANCONFIG (assuming E1/ALAW/CLEAR; unless dchan -> 
HDLCFCS) */
+static int chanconfig(int fd, int channo, bool dchan)
+{
+       struct dahdi_chanconfig dcc = {
+               .chan = channo,
+               .sigtype = DAHDI_SIG_CLEAR,
+               .deflaw = DAHDI_LAW_ALAW,
+               .master = 0,
+               .idlebits = 0,
+       };
+       if (dchan)
+               dcc.sigtype = DAHDI_SIG_HDLCFCS;
+
+       return ioctl(fd, DAHDI_CHANCONFIG, &dcc);
+}
+
+static int chanconfig_hdlc_master(int fd, int channo, const char *netdev_name)
+{
+       struct dahdi_chanconfig dcc = {
+               .chan = channo,
+               .sigtype = DAHDI_SIG_HDLCNET,
+               .deflaw = DAHDI_LAW_ALAW,
+               .master = 0,
+               .idlebits = 0,
+       };
+       strncpy(dcc.netdev_name, netdev_name, sizeof(dcc.netdev_name));
+       dcc.netdev_name[sizeof(dcc.netdev_name)-1] = '\0';
+
+       return ioctl(fd, DAHDI_CHANCONFIG, &dcc);
+}
+
+static int chanconfig_hdlc_slave(int fd, int channo, int master)
+{
+       struct dahdi_chanconfig dcc = {
+               .chan = channo,
+               .sigtype = DAHDI_SIG_SLAVE,
+               .deflaw = DAHDI_LAW_ALAW,
+               .master = master,
+               .idlebits = 0,
+       };
+
+       return ioctl(fd, DAHDI_CHANCONFIG, &dcc);
+}
+
+/* high-level wrapper to configure an E1 span + all its channels */
+static int config(int spanno, const char *hdlc_netdev)
+{
+       struct dahdi_spaninfo dsi = {
+               .spanno = spanno,
+       };
+       int fd, rc;
+
+       fd = open("/dev/dahdi/ctl", O_RDWR);
+       if (fd < 0) {
+               fprintf(stderr, "Unable to open /dev/dahd/ctl: %s\n", 
strerror(errno));
+               return fd;
+       }
+
+       rc = ioctl(fd, DAHDI_SPANSTAT, &dsi);
+       if (rc < 0) {
+               fprintf(stderr, "Error during spanstat: %s\n", strerror(errno));
+               close(fd);
+               return rc;
+       }
+       printf("Span %u (%s: %s)\n", dsi.spanno, dsi.name, dsi.desc);
+       printf("  numchans=%u, totalchans=%u\n", dsi.numchans, dsi.totalchans);
+
+       rc = spanconfig(fd, spanno);
+       if (rc < 0) {
+               fprintf(stderr, "Error during spanconfig: %s\n", 
strerror(errno));
+               close(fd);
+               return rc;
+       }
+
+       /* resolve basechan + num_chan via sysfs */
+       int basechan = dahdi_span_get_basechan(spanno);
+       int num_chan = dahdi_span_get_channels(spanno);
+       if (basechan < 0 || num_chan < 0) {
+               fprintf(stderr, "Error getting basechan=%d or channels=%d for 
spanno=%d: %s\n",
+                       basechan, num_chan, spanno, strerror(errno));
+               close(fd);
+               return -1;
+       }
+
+       for (unsigned int i = basechan; i < basechan+num_chan; i++) {
+               if (!hdlc_netdev) {
+                       /* ISDN: we assume a standard ISDN configuration with 
TS16 == D-channel */
+                       rc = chanconfig(fd, i, i == basechan + 15);
+               } else {
+                       /* HDLC: we assume TS 1-31 as superchannel */
+                       if (i == basechan)
+                               rc = chanconfig_hdlc_master(fd, i, hdlc_netdev);
+                       else
+                               rc = chanconfig_hdlc_slave(fd, i, basechan);
+               }
+               if (rc < 0) {
+                       fprintf(stderr, "Error during spanconfig: %s\n", 
strerror(errno));
+                       close(fd);
+                       return rc;
+               }
+       }
+       close(fd);
+       return 0;
+}
+
+static void print_help(void)
+{
+       printf("\n");
+       printf( "dahdi_trunkdev create NAME\n"
+               "   Create a new dahdi trunkdev named NAME\n"
+               "dahdi_trunkdev delete NAME\n"
+               "   Delete an existing dahdi trunkdev named NAME\n"
+               "dahdi_trunkdev config NAME\n"
+               "   Configure span + all channels of trunkdev named NAME for 
ISDN\n"
+               "dahdi_trunkdev config-hdlc NAME\n"
+               "   Configure span + all channels of trunkdev named NAME for 
HDLC\n"
+               "dahdi_trunkdev read NAME\n"
+               "   Continuously read + discard frames from trunkdev named 
NAME\n"
+               "dahdi_trunkdev rdwr NAME\n"
+               "   Continuously read + loop back (write) frames of trunkdev 
named NAME\n");
+
+}
+
+int main(int argc, char **argv)
+{
+       int rc;
+
+       printf("DAHDI trunkdev tool (C) 2022 by Harald Welte 
<[email protected]>\n\n");
+
+       if (argc < 2) {
+               fprintf(stderr, "First argument must specify the command\n");
+               print_help();
+               exit(2);
+       }
+
+       if (!strcmp(argv[1], "help") || !strcmp(argv[1], "--help")) {
+               print_help();
+               exit(0);
+       }
+
+       g_fd = open("/dev/dahdi/trunkdev", O_RDWR);
+       if (g_fd < 0) {
+               fprintf(stderr, "Unable to open trunkdev: %s\n", 
strerror(errno));
+               exit(1);
+       }
+
+       if (!strcmp(argv[1], "create")) {
+               /* dahdi_trunkdev create foo */
+               if (argc < 3) {
+                       fprintf(stderr, "Second argument must specify the 
name\n");
+                       exit(2);
+               }
+               rc = trunkdev_create(g_fd, argv[2]);
+               if (rc < 0) {
+                       fprintf(stderr, "Unable to create trunkdev: %s\n", 
strerror(errno));
+                       exit(1);
+               }
+               printf("DAHDI trunkdev %s created; span number %d\n", argv[2], 
rc);
+       } else if (!strcmp(argv[1], "delete")) {
+               /* dahdi_trunkdev create foo */
+               if (argc < 3) {
+                       fprintf(stderr, "Second argument must specify the 
name\n");
+                       exit(2);
+               }
+               rc = trunkdev_delete(g_fd, argv[2]);
+               if (rc < 0) {
+                       fprintf(stderr, "Unable to create trunkdev: %s\n", 
strerror(errno));
+                       exit(1);
+               }
+               printf("DAHDI trunkdev %s deleted\n", argv[2]);
+       } else if (!strcmp(argv[1], "config")) {
+               /* dahdi_trunkdev config foo */
+               if (argc < 3) {
+                       fprintf(stderr, "Second argument must specify the 
name\n");
+                       exit(2);
+               }
+               rc = trunkdev_specify(g_fd, argv[2]);
+               if (rc < 0) {
+                       fprintf(stderr, "Unable to specify trunkdev: %s\n", 
strerror(errno));
+                       exit(2);
+               }
+               rc = config(rc, NULL);
+               if (rc < 0)
+                       exit(2);
+               printf("DAHDI trunkdev %s configured\n", argv[2]);
+       } else if (!strcmp(argv[1], "config-hdlc")) {
+               /* dahdi_trunkdev config-hdlc foo hdlc-foo */
+               if (argc < 3) {
+                       fprintf(stderr, "Second argument must specify the 
name\n");
+                       exit(2);
+               }
+               if (argc < 4) {
+                       fprintf(stderr, "Third argument must specify the 
net-device name\n");
+                       exit(2);
+               }
+               rc = trunkdev_specify(g_fd, argv[2]);
+               if (rc < 0) {
+                       fprintf(stderr, "Unable to specify trunkdev: %s\n", 
strerror(errno));
+                       exit(2);
+               }
+               rc = config(rc, argv[3]);
+               if (rc < 0)
+                       exit(2);
+               printf("DAHDI trunkdev %s configured as netdev %s\n", argv[2], 
argv[3]);
+       } else if (!strcmp(argv[1], "read") || !strcmp(argv[1], "rdwr")) {
+               /* dahdi_trunkdev (read|rdwr) foo */
+               if (argc < 3) {
+                       fprintf(stderr, "Second argument must specify the 
name\n");
+                       exit(2);
+               }
+               rc = trunkdev_specify(g_fd, argv[2]);
+               if (rc < 0) {
+                       fprintf(stderr, "Unable to specify trunkdev: %s\n", 
strerror(errno));
+                       exit(1);
+               }
+               if (!strcmp(argv[1], "read"))
+                       printf("Reading + Disccarding from DAHDI trunkdev 
%s\n", argv[2]);
+               else
+                       printf("Reading + Writing (looping back) DAHDI trunkdev 
%s\n", argv[2]);
+               while (1) {
+                       uint8_t buf[32*8];
+                       rc = read(g_fd, buf, sizeof(buf));
+                       if (rc <= 0) {
+                               fprintf(stderr, "Error %d reading: %s\n", rc, 
strerror(errno));
+                               exit(1);
+                       }
+                       fputc('.', stdout);
+                       if (!strcmp(argv[1], "rdwr")) {
+                               rc = write(g_fd, buf, rc);
+                               if (rc <= 0) {
+                                       fprintf(stderr, "Error %d writing: 
%s\n", rc, strerror(errno));
+                                       exit(1);
+                               }
+                       }
+               }
+       } else
+               exit(23);
+
+       exit(0);
+}

--
To view, visit https://gerrit.osmocom.org/c/dahdi-tools/+/34013
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings

Gerrit-Project: dahdi-tools
Gerrit-Branch: master
Gerrit-Change-Id: Ie05100c80fd978e7ac6f5a028e0ef6c828e0bcc3
Gerrit-Change-Number: 34013
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <[email protected]>
Gerrit-MessageType: newchange

Reply via email to