re-structure the OpenBSC directory layout
The new structure divides the code into a number of libraries
for the BSC core functionality, MSC core functionality, Abis transport,
TRAU and other bits.
This doesn't introduce any functional code change but simply moves
around files and alters Makefile.am accordingly.
Next step would be to disentangle a lot of the inter-library
dependencies and make the individual bits of code more independent.
diff --git a/openbsc/src/trau/Makefile.am b/openbsc/src/trau/Makefile.am
new file mode 100644
index 0000000..01ed251
--- /dev/null
+++ b/openbsc/src/trau/Makefile.am
@@ -0,0 +1,7 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
+AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS)
+AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS)
+
+noinst_LIBRARIES = libtrau.a
+
+libtrau_a_SOURCES = rtp_proxy.c subchan_demux.c trau_frame.c trau_mux.c trau_upqueue.c
diff --git a/openbsc/src/trau/rtp_proxy.c b/openbsc/src/trau/rtp_proxy.c
new file mode 100644
index 0000000..eefc0e1
--- /dev/null
+++ b/openbsc/src/trau/rtp_proxy.c
@@ -0,0 +1,728 @@
+/* RTP proxy handling for ip.access nanoBTS */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h> /* gettimeofday() */
+#include <unistd.h> /* get..() */
+#include <time.h> /* clock() */
+#include <sys/utsname.h> /* uname() */
+
+#include <osmocore/talloc.h>
+#include <openbsc/gsm_data.h>
+#include <osmocore/msgb.h>
+#include <osmocore/select.h>
+#include <openbsc/debug.h>
+#include <openbsc/rtp_proxy.h>
+
+/* attempt to determine byte order */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <limits.h>
+
+#ifndef __BYTE_ORDER
+#error "__BYTE_ORDER should be defined by someone"
+#endif
+
+static LLIST_HEAD(rtp_sockets);
+
+/* should we mangle the CNAME inside SDES of RTCP packets? We disable
+ * this by default, as it seems to be not needed */
+static int mangle_rtcp_cname = 0;
+
+enum rtp_bfd_priv {
+ RTP_PRIV_NONE,
+ RTP_PRIV_RTP,
+ RTP_PRIV_RTCP
+};
+
+#define RTP_ALLOC_SIZE 1500
+
+/* according to RFC 1889 */
+struct rtcp_hdr {
+ u_int8_t byte0;
+ u_int8_t type;
+ u_int16_t length;
+} __attribute__((packed));
+
+#define RTCP_TYPE_SDES 202
+
+#define RTCP_IE_CNAME 1
+
+/* according to RFC 3550 */
+struct rtp_hdr {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ u_int8_t csrc_count:4,
+ extension:1,
+ padding:1,
+ version:2;
+ u_int8_t payload_type:7,
+ marker:1;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ u_int8_t version:2,
+ padding:1,
+ extension:1,
+ csrc_count:4;
+ u_int8_t marker:1,
+ payload_type:7;
+#endif
+ u_int16_t sequence;
+ u_int32_t timestamp;
+ u_int32_t ssrc;
+} __attribute__((packed));
+
+struct rtp_x_hdr {
+ u_int16_t by_profile;
+ u_int16_t length;
+} __attribute__((packed));
+
+#define RTP_VERSION 2
+
+/* decode an rtp frame and create a new buffer with payload */
+static int rtp_decode(struct msgb *msg, u_int32_t callref, struct msgb **data)
+{
+ struct msgb *new_msg;
+ struct gsm_data_frame *frame;
+ struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data;
+ struct rtp_x_hdr *rtpxh;
+ u_int8_t *payload;
+ int payload_len;
+ int msg_type;
+ int x_len;
+
+ if (msg->len < 12) {
+ DEBUGPC(DMUX, "received RTP frame too short (len = %d)\n",
+ msg->len);
+ return -EINVAL;
+ }
+ if (rtph->version != RTP_VERSION) {
+ DEBUGPC(DMUX, "received RTP version %d not supported.\n",
+ rtph->version);
+ return -EINVAL;
+ }
+ payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
+ payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
+ if (payload_len < 0) {
+ DEBUGPC(DMUX, "received RTP frame too short (len = %d, "
+ "csrc count = %d)\n", msg->len, rtph->csrc_count);
+ return -EINVAL;
+ }
+ if (rtph->extension) {
+ if (payload_len < sizeof(struct rtp_x_hdr)) {
+ DEBUGPC(DMUX, "received RTP frame too short for "
+ "extension header\n");
+ return -EINVAL;
+ }
+ rtpxh = (struct rtp_x_hdr *)payload;
+ x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
+ payload += x_len;
+ payload_len -= x_len;
+ if (payload_len < 0) {
+ DEBUGPC(DMUX, "received RTP frame too short, "
+ "extension header exceeds frame length\n");
+ return -EINVAL;
+ }
+ }
+ if (rtph->padding) {
+ if (payload_len < 0) {
+ DEBUGPC(DMUX, "received RTP frame too short for "
+ "padding length\n");
+ return -EINVAL;
+ }
+ payload_len -= payload[payload_len - 1];
+ if (payload_len < 0) {
+ DEBUGPC(DMUX, "received RTP frame with padding "
+ "greater than payload\n");
+ return -EINVAL;
+ }
+ }
+
+ switch (rtph->payload_type) {
+ case RTP_PT_GSM_FULL:
+ msg_type = GSM_TCHF_FRAME;
+ if (payload_len != 33) {
+ DEBUGPC(DMUX, "received RTP full rate frame with "
+ "payload length != 32 (len = %d)\n",
+ payload_len);
+ return -EINVAL;
+ }
+ break;
+ case RTP_PT_GSM_EFR:
+ msg_type = GSM_TCHF_FRAME_EFR;
+ break;
+ default:
+ DEBUGPC(DMUX, "received RTP frame with unknown payload "
+ "type %d\n", rtph->payload_type);
+ return -EINVAL;
+ }
+
+ new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + payload_len,
+ "GSM-DATA");
+ if (!new_msg)
+ return -ENOMEM;
+ frame = (struct gsm_data_frame *)(new_msg->data);
+ frame->msg_type = msg_type;
+ frame->callref = callref;
+ memcpy(frame->data, payload, payload_len);
+ msgb_put(new_msg, sizeof(struct gsm_data_frame) + payload_len);
+
+ *data = new_msg;
+ return 0;
+}
+
+/* "to - from" */
+static void tv_difference(struct timeval *diff, const struct timeval *from,
+ const struct timeval *__to)
+{
+ struct timeval _to = *__to, *to = &_to;
+
+ if (to->tv_usec < from->tv_usec) {
+ to->tv_sec -= 1;
+ to->tv_usec += 1000000;
+ }
+
+ diff->tv_usec = to->tv_usec - from->tv_usec;
+ diff->tv_sec = to->tv_sec - from->tv_sec;
+}
+
+/* encode and send a rtp frame */
+int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame)
+{
+ struct rtp_sub_socket *rss = &rs->rtp;
+ struct msgb *msg;
+ struct rtp_hdr *rtph;
+ int payload_type;
+ int payload_len;
+ int duration; /* in samples */
+
+ if (rs->tx_action != RTP_SEND_DOWNSTREAM) {
+ /* initialize sequences */
+ rs->tx_action = RTP_SEND_DOWNSTREAM;
+ rs->transmit.ssrc = rand();
+ rs->transmit.sequence = random();
+ rs->transmit.timestamp = random();
+ }
+
+ switch (frame->msg_type) {
+ case GSM_TCHF_FRAME:
+ payload_type = RTP_PT_GSM_FULL;
+ payload_len = 33;
+ duration = 160;
+ break;
+ case GSM_TCHF_FRAME_EFR:
+ payload_type = RTP_PT_GSM_EFR;
+ payload_len = 31;
+ duration = 160;
+ break;
+ default:
+ DEBUGPC(DMUX, "unsupported message type %d\n",
+ frame->msg_type);
+ return -EINVAL;
+ }
+
+ {
+ struct timeval tv, tv_diff;
+ long int usec_diff, frame_diff;
+
+ gettimeofday(&tv, NULL);
+ tv_difference(&tv_diff, &rs->transmit.last_tv, &tv);
+ rs->transmit.last_tv = tv;
+
+ usec_diff = tv_diff.tv_sec * 1000000 + tv_diff.tv_usec;
+ frame_diff = (usec_diff / 20000);
+
+ if (abs(frame_diff) > 1) {
+ long int frame_diff_excess = frame_diff - 1;
+
+ LOGP(DMUX, LOGL_NOTICE,
+ "Correcting frame difference of %ld frames\n", frame_diff_excess);
+ rs->transmit.sequence += frame_diff_excess;
+ rs->transmit.timestamp += frame_diff_excess * duration;
+ }
+ }
+
+ msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL");
+ if (!msg)
+ return -ENOMEM;
+ rtph = (struct rtp_hdr *)msg->data;
+ rtph->version = RTP_VERSION;
+ rtph->padding = 0;
+ rtph->extension = 0;
+ rtph->csrc_count = 0;
+ rtph->marker = 0;
+ rtph->payload_type = payload_type;
+ rtph->sequence = htons(rs->transmit.sequence++);
+ rtph->timestamp = htonl(rs->transmit.timestamp);
+ rs->transmit.timestamp += duration;
+ rtph->ssrc = htonl(rs->transmit.ssrc);
+ memcpy(msg->data + sizeof(struct rtp_hdr), frame->data, payload_len);
+ msgb_put(msg, sizeof(struct rtp_hdr) + payload_len);
+ msgb_enqueue(&rss->tx_queue, msg);
+ rss->bfd.when |= BSC_FD_WRITE;
+
+ return 0;
+}
+
+/* iterate over all chunks in one RTCP message, look for CNAME IEs and
+ * replace all of those with 'new_cname' */
+static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh,
+ u_int16_t *rtcp_len, const char *new_cname)
+{
+ u_int8_t *rtcp_end;
+ u_int8_t *cur = (u_int8_t *) rh;
+ u_int8_t tag, len = 0;
+
+ rtcp_end = cur + *rtcp_len;
+ /* move cur to end of RTP header */
+ cur += sizeof(*rh);
+
+ /* iterate over Chunks */
+ while (cur+4 < rtcp_end) {
+ /* skip four bytes SSRC/CSRC */
+ cur += 4;
+
+ /* iterate over IE's inside the chunk */
+ while (cur+1 < rtcp_end) {
+ tag = *cur++;
+ if (tag == 0) {
+ /* end of chunk, skip additional zero */
+ while (*cur++ == 0) { }
+ break;
+ }
+ len = *cur++;
+
+ if (tag == RTCP_IE_CNAME) {
+ /* we've found the CNAME, lets mangle it */
+ if (len < strlen(new_cname)) {
+ /* we need to make more space */
+ int increase = strlen(new_cname) - len;
+
+ msgb_push(msg, increase);
+ memmove(cur+len+increase, cur+len,
+ rtcp_end - (cur+len));
+ /* FIXME: we have to respect RTCP
+ * padding/alignment rules! */
+ len += increase;
+ *(cur-1) += increase;
+ rtcp_end += increase;
+ *rtcp_len += increase;
+ }
+ /* copy new CNAME into message */
+ memcpy(cur, new_cname, strlen(new_cname));
+ /* FIXME: zero the padding in case new CNAME
+ * is smaller than old one !!! */
+ }
+ cur += len;
+ }
+ }
+
+ return 0;
+}
+
+static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
+{
+ struct rtp_sub_socket *rss = &rs->rtcp;
+ struct rtcp_hdr *rtph;
+ u_int16_t old_len;
+ int rc;
+
+ if (!mangle_rtcp_cname)
+ return 0;
+
+ printf("RTCP\n");
+ /* iterate over list of RTCP messages */
+ rtph = (struct rtcp_hdr *)msg->data;
+ while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) {
+ old_len = (ntohs(rtph->length) + 1) * 4;
+ if ((void *)rtph + old_len > (void *)msg->data + msg->len) {
+ DEBUGPC(DMUX, "received RTCP packet too short for "
+ "length element\n");
+ return -EINVAL;
+ }
+ if (rtph->type == RTCP_TYPE_SDES) {
+ char new_cname[255];
+ strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr),
+ sizeof(new_cname));
+ new_cname[sizeof(new_cname)-1] = '\0';
+ rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len,
+ new_cname);
+ if (rc < 0)
+ return rc;
+ }
+ rtph = (void *)rtph + old_len;
+ }
+
+ return 0;
+}
+
+/* read from incoming RTP/RTCP socket */
+static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
+{
+ int rc;
+ struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
+ struct msgb *new_msg;
+ struct rtp_sub_socket *other_rss;
+
+ if (!msg)
+ return -ENOMEM;
+
+ rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE);
+ if (rc <= 0) {
+ rss->bfd.when &= ~BSC_FD_READ;
+ return rc;
+ }
+
+ msgb_put(msg, rc);
+
+ switch (rs->rx_action) {
+ case RTP_PROXY:
+ if (!rs->proxy.other_sock) {
+ rc = -EIO;
+ goto out_free;
+ }
+ if (rss->bfd.priv_nr == RTP_PRIV_RTP)
+ other_rss = &rs->proxy.other_sock->rtp;
+ else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
+ other_rss = &rs->proxy.other_sock->rtcp;
+ /* modify RTCP SDES CNAME */
+ rc = rtcp_mangle(msg, rs);
+ if (rc < 0)
+ goto out_free;
+ } else {
+ rc = -EINVAL;
+ goto out_free;
+ }
+ msgb_enqueue(&other_rss->tx_queue, msg);
+ other_rss->bfd.when |= BSC_FD_WRITE;
+ break;
+
+ case RTP_RECV_UPSTREAM:
+ if (!rs->receive.callref || !rs->receive.net) {
+ rc = -EIO;
+ goto out_free;
+ }
+ if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
+ if (!mangle_rtcp_cname) {
+ msgb_free(msg);
+ break;
+ }
+ /* modify RTCP SDES CNAME */
+ rc = rtcp_mangle(msg, rs);
+ if (rc < 0)
+ goto out_free;
+ msgb_enqueue(&rss->tx_queue, msg);
+ rss->bfd.when |= BSC_FD_WRITE;
+ break;
+ }
+ if (rss->bfd.priv_nr != RTP_PRIV_RTP) {
+ rc = -EINVAL;
+ goto out_free;
+ }
+ rc = rtp_decode(msg, rs->receive.callref, &new_msg);
+ if (rc < 0)
+ goto out_free;
+ msgb_free(msg);
+ trau_tx_to_mncc(rs->receive.net, new_msg);
+ break;
+
+ case RTP_NONE: /* if socket exists, but disabled by app */
+ msgb_free(msg);
+ break;
+ }
+
+ return 0;
+
+out_free:
+ msgb_free(msg);
+ return rc;
+}
+
+/* write from tx_queue to RTP/RTCP socket */
+static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss)
+{
+ struct msgb *msg;
+ int written;
+
+ msg = msgb_dequeue(&rss->tx_queue);
+ if (!msg) {
+ rss->bfd.when &= ~BSC_FD_WRITE;
+ return 0;
+ }
+
+ written = write(rss->bfd.fd, msg->data, msg->len);
+ if (written < msg->len) {
+ LOGP(DMIB, LOGL_ERROR, "short write");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ msgb_free(msg);
+
+ return 0;
+}
+
+
+/* callback for the select.c:bfd_* layer */
+static int rtp_bfd_cb(struct bsc_fd *bfd, unsigned int flags)
+{
+ struct rtp_socket *rs = bfd->data;
+ struct rtp_sub_socket *rss;
+
+ switch (bfd->priv_nr) {
+ case RTP_PRIV_RTP:
+ rss = &rs->rtp;
+ break;
+ case RTP_PRIV_RTCP:
+ rss = &rs->rtcp;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (flags & BSC_FD_READ)
+ rtp_socket_read(rs, rss);
+
+ if (flags & BSC_FD_WRITE)
+ rtp_socket_write(rs, rss);
+
+ return 0;
+}
+
+static void init_rss(struct rtp_sub_socket *rss,
+ struct rtp_socket *rs, int fd, int priv_nr)
+{
+ /* initialize bfd */
+ rss->bfd.fd = fd;
+ rss->bfd.data = rs;
+ rss->bfd.priv_nr = priv_nr;
+ rss->bfd.cb = rtp_bfd_cb;
+}
+
+struct rtp_socket *rtp_socket_create(void)
+{
+ int rc;
+ struct rtp_socket *rs;
+
+ DEBUGP(DMUX, "rtp_socket_create(): ");
+
+ rs = talloc_zero(tall_bsc_ctx, struct rtp_socket);
+ if (!rs)
+ return NULL;
+
+ INIT_LLIST_HEAD(&rs->rtp.tx_queue);
+ INIT_LLIST_HEAD(&rs->rtcp.tx_queue);
+
+ rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (rc < 0)
+ goto out_free;
+
+ init_rss(&rs->rtp, rs, rc, RTP_PRIV_RTP);
+ rc = bsc_register_fd(&rs->rtp.bfd);
+ if (rc < 0)
+ goto out_rtp_socket;
+
+ rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (rc < 0)
+ goto out_rtp_bfd;
+
+ init_rss(&rs->rtcp, rs, rc, RTP_PRIV_RTCP);
+ rc = bsc_register_fd(&rs->rtcp.bfd);
+ if (rc < 0)
+ goto out_rtcp_socket;
+
+ DEBUGPC(DMUX, "success\n");
+
+ rc = rtp_socket_bind(rs, INADDR_ANY);
+ if (rc < 0)
+ goto out_rtcp_bfd;
+
+ return rs;
+
+out_rtcp_bfd:
+ bsc_unregister_fd(&rs->rtcp.bfd);
+out_rtcp_socket:
+ close(rs->rtcp.bfd.fd);
+out_rtp_bfd:
+ bsc_unregister_fd(&rs->rtp.bfd);
+out_rtp_socket:
+ close(rs->rtp.bfd.fd);
+out_free:
+ talloc_free(rs);
+ DEBUGPC(DMUX, "failed\n");
+ return NULL;
+}
+
+static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, u_int32_t ip,
+ u_int16_t port)
+{
+ int rc;
+ socklen_t alen = sizeof(rss->sin_local);
+
+ rss->sin_local.sin_family = AF_INET;
+ rss->sin_local.sin_addr.s_addr = htonl(ip);
+ rss->sin_local.sin_port = htons(port);
+ rss->bfd.when |= BSC_FD_READ;
+
+ rc = bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
+ sizeof(rss->sin_local));
+ if (rc < 0)
+ return rc;
+
+ /* retrieve the address we actually bound to, in case we
+ * passed INADDR_ANY as IP address */
+ return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
+ &alen);
+}
+
+#define RTP_PORT_BASE 30000
+static unsigned int next_udp_port = RTP_PORT_BASE;
+
+/* bind a RTP socket to a local address */
+int rtp_socket_bind(struct rtp_socket *rs, u_int32_t ip)
+{
+ int rc = -EIO;
+ struct in_addr ia;
+
+ ia.s_addr = htonl(ip);
+ DEBUGP(DMUX, "rtp_socket_bind(rs=%p, IP=%s): ", rs,
+ inet_ntoa(ia));
+
+ /* try to bind to a consecutive pair of ports */
+ for (next_udp_port = next_udp_port % 0xffff;
+ next_udp_port < 0xffff; next_udp_port += 2) {
+ rc = rtp_sub_socket_bind(&rs->rtp, ip, next_udp_port);
+ if (rc != 0)
+ continue;
+
+ rc = rtp_sub_socket_bind(&rs->rtcp, ip, next_udp_port+1);
+ if (rc == 0)
+ break;
+ }
+ if (rc < 0) {
+ DEBUGPC(DMUX, "failed\n");
+ return rc;
+ }
+
+ ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr;
+ DEBUGPC(DMUX, "BOUND_IP=%s, BOUND_PORT=%u\n",
+ inet_ntoa(ia), ntohs(rs->rtp.sin_local.sin_port));
+ return ntohs(rs->rtp.sin_local.sin_port);
+}
+
+static int rtp_sub_socket_connect(struct rtp_sub_socket *rss,
+ u_int32_t ip, u_int16_t port)
+{
+ int rc;
+ socklen_t alen = sizeof(rss->sin_local);
+
+ rss->sin_remote.sin_family = AF_INET;
+ rss->sin_remote.sin_addr.s_addr = htonl(ip);
+ rss->sin_remote.sin_port = htons(port);
+
+ rc = connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote,
+ sizeof(rss->sin_remote));
+ if (rc < 0)
+ return rc;
+
+ return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
+ &alen);
+}
+
+/* 'connect' a RTP socket to a remote peer */
+int rtp_socket_connect(struct rtp_socket *rs, u_int32_t ip, u_int16_t port)
+{
+ int rc;
+ struct in_addr ia;
+
+ ia.s_addr = htonl(ip);
+ DEBUGP(DMUX, "rtp_socket_connect(rs=%p, ip=%s, port=%u)\n",
+ rs, inet_ntoa(ia), port);
+
+ rc = rtp_sub_socket_connect(&rs->rtp, ip, port);
+ if (rc < 0)
+ return rc;
+
+ return rtp_sub_socket_connect(&rs->rtcp, ip, port+1);
+}
+
+/* bind two RTP/RTCP sockets together */
+int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
+{
+ DEBUGP(DMUX, "rtp_socket_proxy(this=%p, other=%p)\n",
+ this, other);
+
+ this->rx_action = RTP_PROXY;
+ this->proxy.other_sock = other;
+
+ other->rx_action = RTP_PROXY;
+ other->proxy.other_sock = this;
+
+ return 0;
+}
+
+/* bind RTP/RTCP socket to application */
+int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net,
+ u_int32_t callref)
+{
+ DEBUGP(DMUX, "rtp_socket_proxy(this=%p, callref=%u)\n",
+ this, callref);
+
+ if (callref) {
+ this->rx_action = RTP_RECV_UPSTREAM;
+ this->receive.net = net;
+ this->receive.callref = callref;
+ } else
+ this->rx_action = RTP_NONE;
+
+ return 0;
+}
+
+static void free_tx_queue(struct rtp_sub_socket *rss)
+{
+ struct msgb *msg;
+
+ while ((msg = msgb_dequeue(&rss->tx_queue)))
+ msgb_free(msg);
+}
+
+int rtp_socket_free(struct rtp_socket *rs)
+{
+ DEBUGP(DMUX, "rtp_socket_free(rs=%p)\n", rs);
+
+ /* make sure we don't leave references dangling to us */
+ if (rs->rx_action == RTP_PROXY &&
+ rs->proxy.other_sock)
+ rs->proxy.other_sock->proxy.other_sock = NULL;
+
+ bsc_unregister_fd(&rs->rtp.bfd);
+ close(rs->rtp.bfd.fd);
+ free_tx_queue(&rs->rtp);
+
+ bsc_unregister_fd(&rs->rtcp.bfd);
+ close(rs->rtcp.bfd.fd);
+ free_tx_queue(&rs->rtcp);
+
+ talloc_free(rs);
+
+ return 0;
+}
diff --git a/openbsc/src/trau/subchan_demux.c b/openbsc/src/trau/subchan_demux.c
new file mode 100644
index 0000000..6bcf279
--- /dev/null
+++ b/openbsc/src/trau/subchan_demux.c
@@ -0,0 +1,321 @@
+/* A E1 sub-channel (de)multiplexer with TRAU frame sync */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <openbsc/subchan_demux.h>
+#include <openbsc/trau_frame.h>
+#include <openbsc/debug.h>
+#include <osmocore/talloc.h>
+#include <openbsc/gsm_data.h>
+
+void *tall_tqe_ctx;
+
+static inline void append_bit(struct demux_subch *sch, u_int8_t bit)
+{
+ sch->out_bitbuf[sch->out_idx++] = bit;
+}
+
+#define SYNC_HDR_BITS 16
+static const u_int8_t nullbytes[SYNC_HDR_BITS];
+
+/* check if we have just completed the 16 bit zero sync header,
+ * in accordance with GSM TS 08.60 Chapter 4.8.1 */
+static int sync_hdr_complete(struct demux_subch *sch, u_int8_t bit)
+{
+ if (bit == 0)
+ sch->consecutive_zeros++;
+ else
+ sch->consecutive_zeros = 0;
+
+ if (sch->consecutive_zeros >= SYNC_HDR_BITS) {
+ sch->consecutive_zeros = 0;
+ return 1;
+ }
+
+ return 0;
+}
+
+/* resynchronize to current location */
+static void resync_to_here(struct demux_subch *sch)
+{
+ memset(sch->out_bitbuf, 0, SYNC_HDR_BITS);
+
+ /* set index in a way that we can continue receiving bits after
+ * the end of the SYNC header */
+ sch->out_idx = SYNC_HDR_BITS;
+ sch->in_sync = 1;
+}
+
+int subch_demux_init(struct subch_demux *dmx)
+{
+ int i;
+
+ dmx->chan_activ = 0;
+ for (i = 0; i < NR_SUBCH; i++) {
+ struct demux_subch *sch = &dmx->subch[i];
+ sch->out_idx = 0;
+ memset(sch->out_bitbuf, 0xff, sizeof(sch->out_bitbuf));
+ }
+ return 0;
+}
+
+/* input some arbitrary (modulo 4) number of bytes of a 64k E1 channel,
+ * split it into the 16k subchannels */
+int subch_demux_in(struct subch_demux *dmx, u_int8_t *data, int len)
+{
+ int i, c;
+
+ /* we avoid partially filled bytes in outbuf */
+ if (len % 4)
+ return -EINVAL;
+
+ for (i = 0; i < len; i++) {
+ u_int8_t inbyte = data[i];
+
+ for (c = 0; c < NR_SUBCH; c++) {
+ struct demux_subch *sch = &dmx->subch[c];
+ u_int8_t inbits;
+ u_int8_t bit;
+
+ /* ignore inactive subchannels */
+ if (!(dmx->chan_activ & (1 << c)))
+ continue;
+
+ inbits = inbyte >> (c << 1);
+
+ /* two bits for each subchannel */
+ if (inbits & 0x01)
+ bit = 1;
+ else
+ bit = 0;
+ append_bit(sch, bit);
+
+ if (sync_hdr_complete(sch, bit))
+ resync_to_here(sch);
+
+ if (inbits & 0x02)
+ bit = 1;
+ else
+ bit = 0;
+ append_bit(sch, bit);
+
+ if (sync_hdr_complete(sch, bit))
+ resync_to_here(sch);
+
+ /* FIXME: verify the first bit in octet 2, 4, 6, ...
+ * according to TS 08.60 4.8.1 */
+
+ /* once we have reached TRAU_FRAME_BITS, call
+ * the TRAU frame handler callback function */
+ if (sch->out_idx >= TRAU_FRAME_BITS) {
+ if (sch->in_sync) {
+ dmx->out_cb(dmx, c, sch->out_bitbuf,
+ sch->out_idx, dmx->data);
+ sch->in_sync = 0;
+ }
+ sch->out_idx = 0;
+ }
+ }
+ }
+ return i;
+}
+
+int subch_demux_activate(struct subch_demux *dmx, int subch)
+{
+ if (subch >= NR_SUBCH)
+ return -EINVAL;
+
+ dmx->chan_activ |= (1 << subch);
+ return 0;
+}
+
+int subch_demux_deactivate(struct subch_demux *dmx, int subch)
+{
+ if (subch >= NR_SUBCH)
+ return -EINVAL;
+
+ dmx->chan_activ &= ~(1 << subch);
+ return 0;
+}
+
+/* MULTIPLEXER */
+
+static int alloc_add_idle_frame(struct subch_mux *mx, int sch_nr)
+{
+ /* allocate and initialize with idle pattern */
+ return subchan_mux_enqueue(mx, sch_nr, trau_idle_frame(),
+ TRAU_FRAME_BITS);
+}
+
+/* return the requested number of bits from the specified subchannel */
+static int get_subch_bits(struct subch_mux *mx, int subch,
+ u_int8_t *bits, int num_requested)
+{
+ struct mux_subch *sch = &mx->subch[subch];
+ int num_bits = 0;
+
+ while (num_bits < num_requested) {
+ struct subch_txq_entry *txe;
+ int num_bits_left;
+ int num_bits_thistime;
+
+ /* make sure we have a valid entry at top of tx queue.
+ * if not, add an idle frame */
+ if (llist_empty(&sch->tx_queue))
+ alloc_add_idle_frame(mx, subch);
+
+ if (llist_empty(&sch->tx_queue))
+ return -EIO;
+
+ txe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list);
+ num_bits_left = txe->bit_len - txe->next_bit;
+
+ if (num_bits_left < num_requested)
+ num_bits_thistime = num_bits_left;
+ else
+ num_bits_thistime = num_requested;
+
+ /* pull the bits from the txe */
+ memcpy(bits + num_bits, txe->bits + txe->next_bit, num_bits_thistime);
+ txe->next_bit += num_bits_thistime;
+
+ /* free the tx_queue entry if it is fully consumed */
+ if (txe->next_bit >= txe->bit_len) {
+ llist_del(&txe->list);
+ talloc_free(txe);
+ }
+
+ /* increment global number of bits dequeued */
+ num_bits += num_bits_thistime;
+ }
+
+ return num_requested;
+}
+
+/* compact an array of 8 single-bit bytes into one byte of 8 bits */
+static u_int8_t compact_bits(const u_int8_t *bits)
+{
+ u_int8_t ret = 0;
+ int i;
+
+ for (i = 0; i < 8; i++)
+ ret |= (bits[i] ? 1 : 0) << i;
+
+ return ret;
+}
+
+/* obtain a single output byte from the subchannel muxer */
+static int mux_output_byte(struct subch_mux *mx, u_int8_t *byte)
+{
+ u_int8_t bits[8];
+ int rc;
+
+ /* combine two bits of every subchan */
+ rc = get_subch_bits(mx, 0, &bits[0], 2);
+ rc = get_subch_bits(mx, 1, &bits[2], 2);
+ rc = get_subch_bits(mx, 2, &bits[4], 2);
+ rc = get_subch_bits(mx, 3, &bits[6], 2);
+
+ *byte = compact_bits(bits);
+
+ return rc;
+}
+
+/* Request the output of some muxed bytes from the subchan muxer */
+int subchan_mux_out(struct subch_mux *mx, u_int8_t *data, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ int rc;
+ rc = mux_output_byte(mx, &data[i]);
+ if (rc < 0)
+ break;
+ }
+ return i;
+}
+
+static int llist_len(struct llist_head *head)
+{
+ struct llist_head *entry;
+ int i = 0;
+
+ llist_for_each(entry, head)
+ i++;
+
+ return i;
+}
+
+/* evict the 'num_evict' number of oldest entries in the queue */
+static void tx_queue_evict(struct mux_subch *sch, int num_evict)
+{
+ struct subch_txq_entry *tqe;
+ int i;
+
+ for (i = 0; i < num_evict; i++) {
+ if (llist_empty(&sch->tx_queue))
+ return;
+
+ tqe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list);
+ llist_del(&tqe->list);
+ talloc_free(tqe);
+ }
+}
+
+/* enqueue some data into the tx_queue of a given subchannel */
+int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const u_int8_t *data,
+ int len)
+{
+ struct mux_subch *sch = &mx->subch[s_nr];
+ int list_len = llist_len(&sch->tx_queue);
+ struct subch_txq_entry *tqe = talloc_zero_size(tall_tqe_ctx,
+ sizeof(*tqe) + len);
+ if (!tqe)
+ return -ENOMEM;
+
+ tqe->bit_len = len;
+ memcpy(tqe->bits, data, len);
+
+ if (list_len > 2)
+ tx_queue_evict(sch, list_len-2);
+
+ llist_add_tail(&tqe->list, &sch->tx_queue);
+
+ return 0;
+}
+
+/* initialize one subchannel muxer instance */
+int subchan_mux_init(struct subch_mux *mx)
+{
+ int i;
+
+ memset(mx, 0, sizeof(*mx));
+ for (i = 0; i < NR_SUBCH; i++) {
+ struct mux_subch *sch = &mx->subch[i];
+ INIT_LLIST_HEAD(&sch->tx_queue);
+ }
+
+ return 0;
+}
diff --git a/openbsc/src/trau/trau_frame.c b/openbsc/src/trau/trau_frame.c
new file mode 100644
index 0000000..d4d6447
--- /dev/null
+++ b/openbsc/src/trau/trau_frame.c
@@ -0,0 +1,260 @@
+/* TRAU frame handling according to GSM TS 08.60 */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <openbsc/trau_frame.h>
+#include <openbsc/subchan_demux.h>
+#include <openbsc/debug.h>
+
+static u_int32_t get_bits(const u_int8_t *bitbuf, int offset, int num)
+{
+ int i;
+ u_int32_t ret = 0;
+
+ for (i = offset; i < offset + num; i++) {
+ ret = ret << 1;
+ if (bitbuf[i])
+ ret |= 1;
+ }
+ return ret;
+}
+
+/* Decode according to 3.1.1 */
+static void decode_fr(struct decoded_trau_frame *fr, const u_int8_t *trau_bits)
+{
+ int i;
+ int d_idx = 0;
+
+ /* C1 .. C15 */
+ memcpy(fr->c_bits+0, trau_bits+17, 15);
+ /* C16 .. C21 */
+ memcpy(fr->c_bits+15, trau_bits+310, 6);
+ /* T1 .. T4 */
+ memcpy(fr->t_bits+0, trau_bits+316, 4);
+ /* D1 .. D255 */
+ for (i = 32; i < 304; i+= 16) {
+ memcpy(fr->d_bits + d_idx, trau_bits+i+1, 15);
+ d_idx += 15;
+ }
+ /* D256 .. D260 */
+ memcpy(fr->d_bits + d_idx, trau_bits + 305, 5);
+}
+
+/* Decode according to 3.1.2 */
+static void decode_amr(struct decoded_trau_frame *fr, const u_int8_t *trau_bits)
+{
+ int i;
+ int d_idx = 0;
+
+ /* C1 .. C15 */
+ memcpy(fr->c_bits+0, trau_bits+17, 15);
+ /* C16 .. C25 */
+ memcpy(fr->c_bits+15, trau_bits+33, 10);
+ /* T1 .. T4 */
+ memcpy(fr->t_bits+0, trau_bits+316, 4);
+ /* D1 .. D5 */
+ memcpy(fr->d_bits, trau_bits+43, 5);
+ /* D6 .. D245 */
+ for (i = 48; i < 304; i += 16) {
+ memcpy(fr->d_bits + d_idx, trau_bits+i+1, 15);
+ d_idx += 15;
+ }
+ /* D246 .. D256 */
+ memcpy(fr->d_bits + d_idx, trau_bits + 305, 11);
+}
+
+int decode_trau_frame(struct decoded_trau_frame *fr, const u_int8_t *trau_bits)
+{
+ u_int8_t cbits5 = get_bits(trau_bits, 17, 5);
+
+ switch (cbits5) {
+ case TRAU_FT_FR_UP:
+ case TRAU_FT_FR_DOWN:
+ case TRAU_FT_IDLE_UP:
+ case TRAU_FT_IDLE_DOWN:
+ case TRAU_FT_EFR:
+ decode_fr(fr, trau_bits);
+ break;
+ case TRAU_FT_AMR:
+ decode_amr(fr, trau_bits);
+ break;
+ case TRAU_FT_OM_UP:
+ case TRAU_FT_OM_DOWN:
+ case TRAU_FT_DATA_UP:
+ case TRAU_FT_DATA_DOWN:
+ case TRAU_FT_D145_SYNC:
+ case TRAU_FT_EDATA:
+ LOGP(DMUX, LOGL_NOTICE, "can't decode unimplemented TRAU "
+ "Frame Type 0x%02x\n", cbits5);
+ return -1;
+ break;
+ default:
+ LOGP(DMUX, LOGL_NOTICE, "can't decode unknown TRAU "
+ "Frame Type 0x%02x\n", cbits5);
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+const u_int8_t ft_fr_down_bits[] = { 1, 1, 1, 0, 0 };
+const u_int8_t ft_idle_down_bits[] = { 0, 1, 1, 1, 0 };
+
+/* modify an uplink TRAU frame so we can send it downlink */
+int trau_frame_up2down(struct decoded_trau_frame *fr)
+{
+ u_int8_t cbits5 = get_bits(fr->c_bits, 0, 5);
+
+ switch (cbits5) {
+ case TRAU_FT_FR_UP:
+ memcpy(fr->c_bits, ft_fr_down_bits, 5);
+ /* clear time alignment */
+ memset(fr->c_bits+5, 0, 6);
+ /* FIXME: SP / BFI in case of DTx */
+ /* C12 .. C21 are spare and coded as '1' */
+ memset(fr->c_bits+11, 0x01, 10);
+ break;
+ case TRAU_FT_EFR:
+ /* clear time alignment */
+ memset(fr->c_bits+5, 0, 6);
+ /* FIXME: set UFE appropriately */
+ /* FIXME: SP / BFI in case of DTx */
+ break;
+ case TRAU_FT_IDLE_UP:
+ memcpy(fr->c_bits, ft_idle_down_bits, 5);
+ /* clear time alignment */
+ memset(fr->c_bits+5, 0, 6);
+ /* FIXME: SP / BFI in case of DTx */
+ /* C12 .. C21 are spare and coded as '1' */
+ memset(fr->c_bits+11, 0x01, 10);
+ break;
+ case TRAU_FT_FR_DOWN:
+ case TRAU_FT_IDLE_DOWN:
+ case TRAU_FT_OM_DOWN:
+ case TRAU_FT_DATA_DOWN:
+ /* we cannot convert a downlink to a downlink frame */
+ return -EINVAL;
+ break;
+ case TRAU_FT_AMR:
+ case TRAU_FT_OM_UP:
+ case TRAU_FT_DATA_UP:
+ case TRAU_FT_D145_SYNC:
+ case TRAU_FT_EDATA:
+ LOGP(DMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type "
+ "0x%02x\n", cbits5);
+ return -1;
+ break;
+ default:
+ LOGP(DMUX, LOGL_NOTICE, "unknown TRAU Frame Type "
+ "0x%02x\n", cbits5);
+ return -1;
+ break;
+ }
+
+ return 0;
+
+}
+
+static void encode_fr(u_int8_t *trau_bits, const struct decoded_trau_frame *fr)
+{
+ int i;
+ int d_idx = 0;
+
+ trau_bits[16] = 1;
+ /* C1 .. C15 */
+ memcpy(trau_bits+17, fr->c_bits+0, 15);
+ /* D1 .. D255 */
+ for (i = 32; i < 304; i+= 16) {
+ trau_bits[i] = 1;
+ memcpy(trau_bits+i+1, fr->d_bits + d_idx, 15);
+ d_idx += 15;
+ }
+ /* D256 .. D260 */
+ trau_bits[304] = 1;
+ memcpy(trau_bits + 305, fr->d_bits + d_idx, 5);
+ /* C16 .. C21 */
+ memcpy(trau_bits+310, fr->c_bits+15, 6);
+
+ /* FIXME: handle timing adjustment */
+
+ /* T1 .. T4 */
+ memcpy(trau_bits+316, fr->t_bits+0, 4);
+}
+
+
+int encode_trau_frame(u_int8_t *trau_bits, const struct decoded_trau_frame *fr)
+{
+ u_int8_t cbits5 = get_bits(fr->c_bits, 0, 5);
+
+ /* 16 bits of sync header */
+ memset(trau_bits, 0, 16);
+
+ switch (cbits5) {
+ case TRAU_FT_FR_UP:
+ case TRAU_FT_FR_DOWN:
+ case TRAU_FT_IDLE_UP:
+ case TRAU_FT_IDLE_DOWN:
+ case TRAU_FT_EFR:
+ encode_fr(trau_bits, fr);
+ break;
+ case TRAU_FT_AMR:
+ case TRAU_FT_OM_UP:
+ case TRAU_FT_OM_DOWN:
+ case TRAU_FT_DATA_UP:
+ case TRAU_FT_DATA_DOWN:
+ case TRAU_FT_D145_SYNC:
+ case TRAU_FT_EDATA:
+ LOGP(DMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type "
+ "0x%02x\n", cbits5);
+ return -1;
+ break;
+ default:
+ LOGP(DMUX, LOGL_NOTICE, "unknown TRAU Frame Type "
+ "0x%02x\n", cbits5);
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+static struct decoded_trau_frame fr_idle_frame = {
+ .c_bits = { 0, 1, 1, 1, 0 }, /* IDLE DOWNLINK 3.5.5 */
+ .t_bits = { 1, 1, 1, 1 },
+};
+static u_int8_t encoded_idle_frame[TRAU_FRAME_BITS];
+static int dbits_initted;
+
+u_int8_t *trau_idle_frame(void)
+{
+ /* only initialize during the first call */
+ if (!dbits_initted) {
+ /* set all D-bits to 1 */
+ memset(&fr_idle_frame.d_bits, 0x01, 260);
+ encode_fr(encoded_idle_frame, &fr_idle_frame);
+ }
+ return encoded_idle_frame;
+}
diff --git a/openbsc/src/trau/trau_mux.c b/openbsc/src/trau/trau_mux.c
new file mode 100644
index 0000000..712e22d
--- /dev/null
+++ b/openbsc/src/trau/trau_mux.c
@@ -0,0 +1,312 @@
+/* Simple TRAU frame reflector to route voice calls */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/trau_frame.h>
+#include <openbsc/trau_mux.h>
+#include <openbsc/subchan_demux.h>
+#include <openbsc/e1_input.h>
+#include <openbsc/debug.h>
+#include <osmocore/talloc.h>
+
+u_int8_t gsm_fr_map[] = {
+ 6, 6, 5, 5, 4, 4, 3, 3,
+ 7, 2, 2, 6, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 7, 2, 2, 6, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 7, 2, 2, 6, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 7, 2, 2, 6, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3
+};
+
+struct map_entry {
+ struct llist_head list;
+ struct gsm_e1_subslot src, dst;
+};
+
+struct upqueue_entry {
+ struct llist_head list;
+ struct gsm_network *net;
+ struct gsm_e1_subslot src;
+ u_int32_t callref;
+};
+
+static LLIST_HEAD(ss_map);
+static LLIST_HEAD(ss_upqueue);
+
+void *tall_map_ctx, *tall_upq_ctx;
+
+/* map one particular subslot to another subslot */
+int trau_mux_map(const struct gsm_e1_subslot *src,
+ const struct gsm_e1_subslot *dst)
+{
+ struct map_entry *me;
+
+ me = talloc(tall_map_ctx, struct map_entry);
+ if (!me) {
+ LOGP(DMIB, LOGL_FATAL, "Out of memory\n");
+ return -ENOMEM;
+ }
+
+ DEBUGP(DCC, "Setting up TRAU mux map between (e1=%u,ts=%u,ss=%u) "
+ "and (e1=%u,ts=%u,ss=%u)\n",
+ src->e1_nr, src->e1_ts, src->e1_ts_ss,
+ dst->e1_nr, dst->e1_ts, dst->e1_ts_ss);
+
+ /* make sure to get rid of any stale old mappings */
+ trau_mux_unmap(src, 0);
+ trau_mux_unmap(dst, 0);
+
+ memcpy(&me->src, src, sizeof(me->src));
+ memcpy(&me->dst, dst, sizeof(me->dst));
+ llist_add(&me->list, &ss_map);
+
+ return 0;
+}
+
+int trau_mux_map_lchan(const struct gsm_lchan *src,
+ const struct gsm_lchan *dst)
+{
+ struct gsm_e1_subslot *src_ss, *dst_ss;
+
+ src_ss = &src->ts->e1_link;
+ dst_ss = &dst->ts->e1_link;
+
+ return trau_mux_map(src_ss, dst_ss);
+}
+
+
+/* unmap one particular subslot from another subslot */
+int trau_mux_unmap(const struct gsm_e1_subslot *ss, u_int32_t callref)
+{
+ struct map_entry *me, *me2;
+ struct upqueue_entry *ue, *ue2;
+
+ if (ss)
+ llist_for_each_entry_safe(me, me2, &ss_map, list) {
+ if (!memcmp(&me->src, ss, sizeof(*ss)) ||
+ !memcmp(&me->dst, ss, sizeof(*ss))) {
+ llist_del(&me->list);
+ return 0;
+ }
+ }
+ llist_for_each_entry_safe(ue, ue2, &ss_upqueue, list) {
+ if (ue->callref == callref) {
+ llist_del(&ue->list);
+ return 0;
+ }
+ if (ss && !memcmp(&ue->src, ss, sizeof(*ss))) {
+ llist_del(&ue->list);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+/* look-up an enty in the TRAU mux map */
+static struct gsm_e1_subslot *
+lookup_trau_mux_map(const struct gsm_e1_subslot *src)
+{
+ struct map_entry *me;
+
+ llist_for_each_entry(me, &ss_map, list) {
+ if (!memcmp(&me->src, src, sizeof(*src)))
+ return &me->dst;
+ if (!memcmp(&me->dst, src, sizeof(*src)))
+ return &me->src;
+ }
+ return NULL;
+}
+
+/* look-up an enty in the TRAU upqueue */
+struct upqueue_entry *
+lookup_trau_upqueue(const struct gsm_e1_subslot *src)
+{
+ struct upqueue_entry *ue;
+
+ llist_for_each_entry(ue, &ss_upqueue, list) {
+ if (!memcmp(&ue->src, src, sizeof(*src)))
+ return ue;
+ }
+ return NULL;
+}
+
+static const u_int8_t c_bits_check[] = { 0, 0, 0, 1, 0 };
+
+/* we get called by subchan_demux */
+int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
+ const u_int8_t *trau_bits, int num_bits)
+{
+ struct decoded_trau_frame tf;
+ u_int8_t trau_bits_out[TRAU_FRAME_BITS];
+ struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss);
+ struct subch_mux *mx;
+ struct upqueue_entry *ue;
+ int rc;
+
+ /* decode TRAU, change it to downlink, re-encode */
+ rc = decode_trau_frame(&tf, trau_bits);
+ if (rc)
+ return rc;
+
+ if (!dst_e1_ss) {
+ struct msgb *msg;
+ struct gsm_data_frame *frame;
+ unsigned char *data;
+ int i, j, k, l, o;
+ /* frame shall be sent to upqueue */
+ if (!(ue = lookup_trau_upqueue(src_e1_ss)))
+ return -EINVAL;
+ if (!ue->callref)
+ return -EINVAL;
+ if (memcmp(tf.c_bits, c_bits_check, sizeof(c_bits_check)))
+ DEBUGPC(DMUX, "illegal trau (C1-C5) %s\n",
+ hexdump(tf.c_bits, sizeof(c_bits_check)));
+ msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33,
+ "GSM-DATA");
+ if (!msg)
+ return -ENOMEM;
+
+ frame = (struct gsm_data_frame *)msg->data;
+ memset(frame, 0, sizeof(struct gsm_data_frame));
+ data = frame->data;
+ data[0] = 0xd << 4;
+ /* reassemble d-bits */
+ i = 0; /* counts bits */
+ j = 4; /* counts output bits */
+ k = gsm_fr_map[0]-1; /* current number bit in element */
+ l = 0; /* counts element bits */
+ o = 0; /* offset input bits */
+ while (i < 260) {
+ data[j/8] |= (tf.d_bits[k+o] << (7-(j%8)));
+ if (--k < 0) {
+ o += gsm_fr_map[l];
+ k = gsm_fr_map[++l]-1;
+ }
+ i++;
+ j++;
+ }
+ frame->msg_type = GSM_TCHF_FRAME;
+ frame->callref = ue->callref;
+ trau_tx_to_mncc(ue->net, msg);
+
+ return 0;
+ }
+
+ mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
+ if (!mx)
+ return -EINVAL;
+
+ trau_frame_up2down(&tf);
+ encode_trau_frame(trau_bits_out, &tf);
+
+ /* and send it to the muxer */
+ return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out,
+ TRAU_FRAME_BITS);
+}
+
+/* add receiver instance for lchan and callref */
+int trau_recv_lchan(struct gsm_lchan *lchan, u_int32_t callref)
+{
+ struct gsm_e1_subslot *src_ss;
+ struct upqueue_entry *ue;
+
+ ue = talloc(tall_upq_ctx, struct upqueue_entry);
+ if (!ue)
+ return -ENOMEM;
+
+ src_ss = &lchan->ts->e1_link;
+
+ DEBUGP(DCC, "Setting up TRAU receiver (e1=%u,ts=%u,ss=%u) "
+ "and (callref 0x%x)\n",
+ src_ss->e1_nr, src_ss->e1_ts, src_ss->e1_ts_ss,
+ callref);
+
+ /* make sure to get rid of any stale old mappings */
+ trau_mux_unmap(src_ss, callref);
+
+ memcpy(&ue->src, src_ss, sizeof(ue->src));
+ ue->net = lchan->ts->trx->bts->network;
+ ue->callref = callref;
+ llist_add(&ue->list, &ss_upqueue);
+
+ return 0;
+}
+
+int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame)
+{
+ u_int8_t trau_bits_out[TRAU_FRAME_BITS];
+ struct gsm_e1_subslot *dst_e1_ss = &lchan->ts->e1_link;
+ struct subch_mux *mx;
+ int i, j, k, l, o;
+ unsigned char *data = frame->data;
+ struct decoded_trau_frame tf;
+
+ mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
+ if (!mx)
+ return -EINVAL;
+
+ switch (frame->msg_type) {
+ case GSM_TCHF_FRAME:
+ /* set c-bits and t-bits */
+ tf.c_bits[0] = 1;
+ tf.c_bits[1] = 1;
+ tf.c_bits[2] = 1;
+ tf.c_bits[3] = 0;
+ tf.c_bits[4] = 0;
+ memset(&tf.c_bits[5], 0, 6);
+ memset(&tf.c_bits[11], 1, 10);
+ memset(&tf.t_bits[0], 1, 4);
+ /* reassemble d-bits */
+ i = 0; /* counts bits */
+ j = 4; /* counts input bits */
+ k = gsm_fr_map[0]-1; /* current number bit in element */
+ l = 0; /* counts element bits */
+ o = 0; /* offset output bits */
+ while (i < 260) {
+ tf.d_bits[k+o] = (data[j/8] >> (7-(j%8))) & 1;
+ if (--k < 0) {
+ o += gsm_fr_map[l];
+ k = gsm_fr_map[++l]-1;
+ }
+ i++;
+ j++;
+ }
+ break;
+ default:
+ DEBUGPC(DMUX, "unsupported message type %d\n",
+ frame->msg_type);
+ return -EINVAL;
+ }
+
+ encode_trau_frame(trau_bits_out, &tf);
+
+ /* and send it to the muxer */
+ return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out,
+ TRAU_FRAME_BITS);
+}
diff --git a/openbsc/src/trau/trau_upqueue.c b/openbsc/src/trau/trau_upqueue.c
new file mode 100644
index 0000000..f8edaf0
--- /dev/null
+++ b/openbsc/src/trau/trau_upqueue.c
@@ -0,0 +1,27 @@
+/* trau_upqueue.c - Pass msgb's up the chain */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <openbsc/mncc.h>
+#include <openbsc/gsm_data.h>
+
+void trau_tx_to_mncc(struct gsm_network *net, struct msgb *msg)
+{
+ net->mncc_recv(net, msg);
+}