osmo-isdntap/src/dahdi.c

324 lines
8.2 KiB
C

/* dahdi.c - support for DAHDI TDM interfaces
*
* (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 <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <dahdi/user.h>
#include <osmocom/core/utils.h>
#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,
};