/* dahdi.c - support for DAHDI TDM interfaces * * (C) 2022 by Harald Welte * * 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 #include #include #include #include #include #include #include #include #include #include #include "isdntap.h" #include "log.h" #define BLOCK_SIZE 512 #define DAHDI_SYSFS_ATTR "/sys/bus/dahdi_spans/devices/span-%u/%s" /* obtain a per-span sysfs integer attribute */ static int get_span_sysfs_int(unsigned int spanno, const char *attr) { int res, rc; char filename[PATH_MAX]; FILE *fp; snprintf(filename, sizeof(filename), DAHDI_SYSFS_ATTR, spanno, attr); fp = fopen(filename, "r"); if (!fp) return -1; rc = fscanf(fp, "%d", &res); fclose(fp); if (rc == EOF) return -1; return res; } /* obtain a per-span sysfs string attribute. Caller must free() return value */ static char *get_span_sysfs_str(unsigned int spanno, const char *attr) { char *res = NULL; int rc; char filename[PATH_MAX]; FILE *fp; snprintf(filename, sizeof(filename), DAHDI_SYSFS_ATTR, spanno, attr); fp = fopen(filename, "r"); if (!fp) return NULL; rc = fscanf(fp, "%ms", &res); fclose(fp); if (rc == EOF) { free(res); return NULL; } return res; } /* Find the DAHDI span number for the given 'name' */ static int get_span_no_by_name(const char *name) { unsigned int i; /* iterate over all (up to 256 max) spans and find a matching name */ for (i = 1; i < 256; i++) { char *name_attr = get_span_sysfs_str(i, "name"); if (!name_attr) continue; if (!strcmp(name_attr, name)) { free(name_attr); return i; } free(name_attr); } return -1; } /* crate a [rx or tx] mirror FD for given DAHDI channel number */ static int make_mirror(long type, int chan) { int res = 0; int fd = 0; struct dahdi_bufferinfo bi; fd = open("/dev/dahdi/pseudo", O_RDONLY); memset(&bi, 0, sizeof(bi)); bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE; bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE; bi.numbufs = 32; bi.bufsize = BLOCK_SIZE; ioctl(fd, DAHDI_SET_BUFINFO, &bi); res = ioctl(fd, type, &chan); if (res) { fprintf(stderr, "error setting channel err=%d!\n", res); return -1; } return fd; } static int dahdi_bchan_fd_cb(struct osmo_fd *ofd, unsigned int what) { struct isdntap_ts *ts = ofd->data; uint8_t buf[4096]; int rc; OSMO_ASSERT(what & OSMO_FD_READ); rc = read(ofd->fd, buf, sizeof(buf)); if (rc <= 0) return -EIO; if (ofd->priv_nr == 1) { /* Rx Mirror */ return isdntap_ts_rx_bchan(ts, buf, rc, true); } else { /* Tx Mirror */ return isdntap_ts_rx_bchan(ts, buf, rc, false); } return isdntap_ts_rx_bchan(ts, buf, rc, 0); } static int dahdi_dchan_fd_cb(struct osmo_fd *ofd, unsigned int what) { struct isdntap_ts *ts = ofd->data; uint8_t buf[4096]; int rc; OSMO_ASSERT(what & OSMO_FD_READ); rc = read(ofd->fd, buf, sizeof(buf)); if (rc <= 0) return -EIO; if (ofd->priv_nr == 1) { /* Rx Mirror */ isdntap_ts_rx_dchan(ts, buf, rc, true); } else { /* Tx Mirror */ isdntap_ts_rx_dchan(ts, buf, rc, false); } return isdntap_ts_rx_dchan(ts, buf, rc, 0); } static int open_line_dahdi(struct isdntap_line *line) { struct isdntap_ts *sig_ts; char *spantype; int rc; /* make sure we have a span number */ if (line->drvdata.dahdi.spanno < 0) { if (!line->drvdata.dahdi.name) { LOGPLI(line, DLINP, LOGL_ERROR, "DAHDI name nor span number specified\n"); return -1; } line->drvdata.dahdi.spanno = get_span_no_by_name(line->drvdata.dahdi.name); } if (line->drvdata.dahdi.spanno < 0) { LOGPLI(line, DLINP, LOGL_ERROR, "Unable to resolve DAHDI span number\n"); return -1; } /* resolve the base channel number */ rc = get_span_sysfs_int(line->drvdata.dahdi.spanno, "basechan"); if (rc < 0) { LOGPLI(line, DLINP, LOGL_ERROR, "Unable to get DAHDI basechan via sysfs\n"); return rc; } line->drvdata.dahdi.basechan = rc; /* resolve the numberof channels */ rc = get_span_sysfs_int(line->drvdata.dahdi.spanno, "channels"); if (rc < 0) { LOGPLI(line, DLINP, LOGL_ERROR, "Unable to get DAHDI channels via sysfs\n"); return rc; } line->drvdata.dahdi.channels = rc; /* obtain the span type */ spantype = get_span_sysfs_str(line->drvdata.dahdi.spanno, "spantype"); if (!spantype) { LOGPLI(line, DLINP, LOGL_ERROR, "Unable to get DAHDI spantype via sysfs\n"); return -1; } line->drvdata.dahdi.spantype = talloc_strdup(line, spantype); free(spantype); /* note the DAHDI channel number for each timeslot */ for (int i = 0; i < line->drvdata.dahdi.channels; i++) { /* FIXME: the below is very E1-specific */ struct isdntap_ts *ts = &line->ts[1+i]; ts->drvdata.dahdi.channo = line->drvdata.dahdi.basechan + i; } sig_ts = &line->ts[16]; // FIXME: configurable rc = make_mirror(DAHDI_RXMIRROR, sig_ts->drvdata.dahdi.channo); if (rc < 0) { LOGPTS(sig_ts, DLINP, LOGL_ERROR, "Unable to create RXMIRROR for channo %u\n", sig_ts->drvdata.dahdi.channo); return rc; } /* verify HDLC mode? */ osmo_fd_setup(&sig_ts->drvdata.dahdi.rx, rc, OSMO_FD_READ, dahdi_dchan_fd_cb, sig_ts, 1); osmo_fd_register(&sig_ts->drvdata.dahdi.rx); rc = make_mirror(DAHDI_TXMIRROR, sig_ts->drvdata.dahdi.channo); if (rc < 0) { LOGPTS(sig_ts, DLINP, LOGL_ERROR, "Unable to create TXMIRROR for channo %u\n", sig_ts->drvdata.dahdi.channo); close(sig_ts->drvdata.dahdi.rx.fd); sig_ts->drvdata.dahdi.rx.fd = -1; return rc; } /* verify HDLC mode? */ osmo_fd_setup(&sig_ts->drvdata.dahdi.tx, rc, OSMO_FD_READ, dahdi_dchan_fd_cb, sig_ts, 2); osmo_fd_register(&sig_ts->drvdata.dahdi.tx); LOGPLI(line, DLINP, LOGL_NOTICE, "Successfully opened DAHDI line (and its D-channel)\n"); return 0; } static int open_ts_dahdi(struct isdntap_ts *ts) { int rc; /* don't open it a second time */ if (ts->drvdata.dahdi.rx.fd >= 0) return -EBUSY; rc = make_mirror(DAHDI_RXMIRROR, ts->drvdata.dahdi.channo); if (rc < 0) { LOGPTS(ts, DLINP, LOGL_ERROR, "Unable to create RXMIRROR for channo %u\n", ts->drvdata.dahdi.channo); return rc; } osmo_fd_setup(&ts->drvdata.dahdi.rx, rc, OSMO_FD_READ, dahdi_bchan_fd_cb, ts, 1); osmo_fd_register(&ts->drvdata.dahdi.rx); rc = make_mirror(DAHDI_TXMIRROR, ts->drvdata.dahdi.channo); if (rc < 0) { LOGPTS(ts, DLINP, LOGL_ERROR, "Unable to create TXMIRROR for channo %u\n", ts->drvdata.dahdi.channo); osmo_fd_unregister(&ts->drvdata.dahdi.rx); close(ts->drvdata.dahdi.rx.fd); ts->drvdata.dahdi.rx.fd = -1; return rc; } osmo_fd_setup(&ts->drvdata.dahdi.tx, rc, OSMO_FD_READ, dahdi_bchan_fd_cb, ts, 2); osmo_fd_register(&ts->drvdata.dahdi.tx); LOGPTS(ts, DLINP, LOGL_INFO, "Successfully opened DAHDI timeslot (B-channel)\n"); return 0; } static void close_ts_dahdi(struct isdntap_ts *ts) { if (ts->drvdata.dahdi.rx.fd >= 0) { osmo_fd_unregister(&ts->drvdata.dahdi.rx); close(ts->drvdata.dahdi.rx.fd); ts->drvdata.dahdi.rx.fd = -1; } if (ts->drvdata.dahdi.tx.fd >= 0) { osmo_fd_unregister(&ts->drvdata.dahdi.tx); close(ts->drvdata.dahdi.tx.fd); ts->drvdata.dahdi.tx.fd = -1; } LOGPTS(ts, DLINP, LOGL_INFO, "Successfully closed DAHDI timeslot (B-channel)\n"); } static void close_line_dahdi(struct isdntap_line *line) { /* close all file descriptors on all timeslots */ for (int i = 0; i < line->drvdata.dahdi.channels; i++) { /* FIXME: the below is very E1-specific */ struct isdntap_ts *ts = &line->ts[1+i]; close_ts_dahdi(ts); } LOGPLI(line, DLINP, LOGL_INFO, "Successfully closed DAHDI line (and its D-channel)\n"); } const struct isdntap_driver dahdi_driver = { .name = "dahdi", .line_open = open_line_dahdi, .line_close = close_line_dahdi, .ts_open = open_ts_dahdi, .ts_close = close_ts_dahdi, };