/* (C) 2019 by Harald Welte * All Rights Reserved * * SPDX-License-Identifier: GPL-2.0-or-later * * 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. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "internal.h" static struct test_state g_tst; static int g_prbs_offs_rx; static int g_prbs_offs_tx; static int e1_fd_cb(struct osmo_fd *ofd, unsigned int what) { struct timeslot_state *ts = ofd->data; uint8_t buf[4096]; int rc, len; OSMO_ASSERT(what & OSMO_FD_READ); /* read whatever data */ rc = read(ofd->fd, buf, sizeof(buf)); if (rc < 0) { fprintf(stderr, "E1TS(%d) read: %d (%s)\n", ofd->priv_nr, rc, strerror(errno)); return rc; } len = rc; process_rx(&ts->rx, ofd->priv_nr, buf, len); /* if this is the first cb, we preload the transmit queue a bit */ if (!ts->tx.active) { process_tx(ts, 1024); ts->tx.active = true; } /* generate as many bytes as were read */ process_tx(ts, len); return 0; } static void init_timeslot(struct timeslot_state *ts) { osmo_fd_register(&ts->ofd); printf("E1TS(%02u) opened\n", ts->ofd.priv_nr); ts_init_prbs_tx(ts, g_prbs_offs_tx); ts_init_prbs_rx(ts, g_prbs_offs_rx); } static int open_slots_e1d(struct test_state *tst, int intf_nr, int line_nr) { struct osmo_e1dp_client *clnt = osmo_e1dp_client_create(NULL, E1DP_DEFAULT_SOCKET); int i, rc, num_slots = 0; if (!clnt) { fprintf(stderr, "Unable to connect to osmo-e1d\n"); return -1; } for (i = 1; i < 32; i++) { struct timeslot_state *ts; rc = osmo_e1dp_client_ts_open(clnt, intf_nr, line_nr, i, E1DP_TSMODE_RAW, 1024); if (rc < 0) { fprintf(stderr, "Error opening %d: %d (%s)\n", i, rc, strerror(errno)); return -1; } ts = &tst->ts[tst->next_unused_ts++]; /* open the respective file descriptor */ osmo_fd_setup(&ts->ofd, rc, OSMO_FD_READ, e1_fd_cb, ts, i); init_timeslot(ts); num_slots++; } return num_slots; } static int open_slots(struct test_state *tst, char *basedir) { DIR *dir; struct dirent *ent; int rc, num_slots = 0; if (!strncmp(basedir, "e1d", 3)) { int intf = 0, line = 0; char *intf_str, *line_str; strtok(basedir, ":"); intf_str = strtok(NULL, ":"); if (intf_str) { intf = atoi(intf_str); line_str = strtok(NULL, ":"); if (line_str) line = atoi(line_str); } return open_slots_e1d(tst, intf, line); } dir = opendir(basedir); if (!dir) return -ENOENT; while ((ent = readdir(dir))) { struct timeslot_state *ts; switch (ent->d_type) { case DT_CHR: case DT_FIFO: case DT_SOCK: break; default: printf("%s: skipping\n", ent->d_name); continue; } rc = openat(dirfd(dir), ent->d_name, O_RDWR); if (rc < 0) { fprintf(stderr, "Error opening %s: %d (%s)\n", ent->d_name, rc, strerror(errno)); return -1; } ts = &tst->ts[tst->next_unused_ts++]; /* open the respective file descriptor */ osmo_fd_setup(&ts->ofd, rc, OSMO_FD_READ, e1_fd_cb, ts, atoi(ent->d_name)); cfg_dahdi_buffer(ts->ofd.fd); struct dahdi_bufferinfo bi; rc = ioctl(ts->ofd.fd, DAHDI_GET_BUFINFO, &bi); OSMO_ASSERT(rc == 0); printf("tx_pol=%d, rx_pol=%d, num=%d, size=%d, nread=%d, nwrite=%d\n", bi.txbufpolicy, bi.rxbufpolicy, bi.numbufs, bi.bufsize, bi.readbufs, bi.writebufs); init_timeslot(ts); num_slots++; } closedir(dir); return num_slots; } static void print_report(void) { struct timespec ts_now; int i; clock_gettime(CLOCK_MONOTONIC, &ts_now); for (i = 0; i < ARRAY_SIZE(g_tst.ts); i++) { const struct timeslot_state *ts = &g_tst.ts[i]; printf("E1TS(%02u) STATS: sync_losses=%u, bit_errs=%u in %lu seconds\n", ts->ofd.priv_nr, ts->rx.sync_state.num_sync_loss, ts->rx.sync_state.num_bit_err, ts_now.tv_sec - ts->rx.sync_state.ts_sync.tv_sec); } } static int g_ctrlc_count = 0; static void sig_handler(int signal) { switch (signal) { case SIGINT: g_ctrlc_count++; if (g_ctrlc_count == 1) { print_report(); printf("\nPlease stop remote end before pressing Ctrl+C another time\n"); } if (g_ctrlc_count > 1) exit(0); break; case SIGHUP: print_report(); break; } } static void handle_options(int argc, char **argv) { while (1) { int c; static const struct option long_opts[] = { { "rx-prbs-offset", 1, 0, 'r' }, { "tx-prbs-offset", 1, 0, 't' }, { 0, 0, 0, 0 } }; c = getopt_long(argc, argv, "r:t:", long_opts, NULL); if (c == -1) break; switch (c) { case 'r': g_prbs_offs_rx = atoi(optarg); break; case 't': g_prbs_offs_tx = atoi(optarg); break; default: exit(1); } } } int main(int argc, char **argv) { char *basedir; int rc; void *g_ctx; handle_options(argc, argv); if (argc <= optind) { fprintf(stderr, "You must specify the base-path of your DAHDI span " "like /dev/dahdi/chan/001 or e1d:0:0\n"); exit(1); } basedir = argv[optind]; g_ctx = talloc_named_const(NULL, 0, "g_ctx"); OSMO_ASSERT(g_ctx); osmo_init_logging2(g_ctx, NULL); set_realtime(10); rc = open_slots(&g_tst, basedir); if (rc < 0) exit(1); printf("==> opened a total of %d slots\n", rc); signal(SIGINT, sig_handler); signal(SIGHUP, sig_handler); while (1) { osmo_select_main(0); } }