* add trau_mux implementation to relay from one incoming TRAU

channel to another one (simple voice call switching)
* add a way more generic E1 input layer, abstracting out the misdn
  low-level interface. This also adds infrastructure for multiple TRX
  in one BTS, as well as multiple BTS on one E1 link
* add a E1 subchannel multiplexer for sending multiple 16kbit sub-channels
  one one 64kBps E1 channel
* add TRAU IDLE frame generation
* terminate bsc_hack in case there is a E1 / mISDN init error
* introduce 'e1_config.c' file with static configuration of our
  E1 setup (which TRX/BTS is configured for which TEI/SAPI/E1). This should
  later become a config file rather than a compiled C file.

WARNING: all this compiles but is not tested yet.  Expect fix-up committs over
the next hours or so
This commit is contained in:
Harald Welte 2009-02-09 18:13:26 +00:00
parent 0b4c34e3ca
commit 1fa60c8ab0
15 changed files with 1534 additions and 28 deletions

View File

@ -486,5 +486,6 @@ int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname,
u_int8_t win_size, int forced, gsm_cbfn *cbfn);
int abis_nm_bs11_set_ext_time(struct gsm_bts *bts);
int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect);
int abis_nm_bs11_restart(struct gsm_bts *bts);
#endif /* _NM_H */

137
include/openbsc/e1_input.h Normal file
View File

@ -0,0 +1,137 @@
#ifndef _E1_INPUT_H
#define _E1_INPUT_H
#include <stdlib.h>
#include <openbsc/linuxlist.h>
#include <openbsc/gsm_data.h>
#include <openbsc/msgb.h>
#include <openbsc/select.h>
#include <openbsc/subchan_demux.h>
#define NUM_E1_TS 32
enum e1inp_sign_type {
E1INP_SIGN_NONE,
E1INP_SIGN_OML,
E1INP_SIGN_RSL,
};
struct e1inp_ts;
struct e1inp_sign_link {
/* list of signalling links */
struct llist_head list;
/* to which timeslot do we belong? */
struct e1inp_ts *ts;
enum e1inp_sign_type type;
/* trx for msg->trx of received msgs */
struct gsm_bts_trx *trx;
/* msgb queue of to-be-transmitted msgs */
struct llist_head tx_list;
/* SAPI and TEI on the E1 TS */
u_int8_t sapi;
u_int8_t tei;
union {
struct {
u_int8_t channel;
} misdn;
} driver;
};
enum e1inp_ts_type {
E1INP_TS_TYPE_NONE,
E1INP_TS_TYPE_SIGN,
E1INP_TS_TYPE_TRAU,
};
/* A timeslot in the E1 interface */
struct e1inp_ts {
enum e1inp_ts_type type;
int num;
/* to which line do we belong ? */
struct e1inp_line *line;
union {
struct {
struct llist_head sign_links;
} sign;
struct {
/* subchannel demuxer for frames from E1 */
struct subch_demux demux;
/* subchannel muxer for frames to E1 */
struct subch_mux mux;
} trau;
};
union {
struct {
/* mISDN driver has one fd for each ts */
struct bsc_fd fd;
} misdn;
} driver;
};
struct e1inp_driver {
struct llist_head list;
const char *name;
int (*want_write)(struct e1inp_ts *ts);
};
struct e1inp_line {
struct llist_head list;
unsigned int num;
const char *name;
/* array of timestlots */
struct e1inp_ts ts[NUM_E1_TS];
struct e1inp_driver *driver;
void *driver_data;
};
/* register a driver with the E1 core */
int e1inp_driver_register(struct e1inp_driver *drv);
/* register a line with the E1 core */
int e1inp_line_register(struct e1inp_line *line);
/* find a sign_link for given TEI and SAPI in a TS */
struct e1inp_sign_link *
e1inp_lookup_sign_link(struct e1inp_ts *ts, u_int8_t tei,
u_int8_t sapi);
/* create a new signalling link in a E1 timeslot */
struct e1inp_sign_link *
e1inp_sign_link_create(struct e1inp_ts *ts, enum e1inp_sign_type type,
struct gsm_bts_trx *trx, u_int8_t tei,
u_int8_t sapi);
/* configure and initialize one e1inp_ts */
int e1inp_ts_config(struct e1inp_ts *ts, struct e1inp_line *line,
enum e1inp_ts_type type);
/* Call from the Stack: configuration of this TS has changed */
int e1inp_update_ts(struct e1inp_ts *ts);
/* Receive a packet from the E1 driver */
int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
u_int8_t tei, u_int8_t sapi);
/* called by driver if it wants to transmit on a given TS */
struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts,
struct e1inp_sign_link **sign_link);
/* called by driver in case some kind of link state event */
int e1inp_event(struct e1inp_ts *ts, int evt, u_int8_t tei, u_int8_t sapi);
/* called by TRAU muxer to obtain the destination mux entity */
struct subch_mux *e1inp_get_mux(u_int8_t e1_nr, u_int8_t ts_nr);
#endif /* _E1_INPUT_H */

View File

@ -166,6 +166,8 @@ struct gsm_bts_trx {
struct gsm_bts *bts;
/* number of this TRX in the BTS */
u_int8_t nr;
/* how do we talk RSL with this TRX? */
struct e1inp_sign_link *rsl_link;
u_int16_t arfcn;
struct gsm_bts_trx_ts ts[TRX_NR_TS];
@ -215,6 +217,8 @@ struct gsm_bts {
u_int8_t location_area_code;
/* type of BTS */
enum gsm_bts_type type;
/* how do we talk OML with this TRX? */
struct e1inp_sign_link *oml_link;
/* Abis network management O&M handle */
struct abis_nm_h *nmh;
@ -255,10 +259,8 @@ const char *gsm_chreq_name(enum gsm_chreq_reason_t c);
enum gsm_e1_event {
EVT_E1_NONE,
EVT_E1_OML_UP,
EVT_E1_RSL_UP,
EVT_E1_OML_DN,
EVT_E1_RSL_DN,
EVT_E1_TEI_UP,
EVT_E1_TEI_DN,
};
#endif

View File

@ -1,6 +1,6 @@
#ifndef _SUBCH_DEMUX_H
#define _SUBCH_DEMUX_H
/* A E1 sub-channel demultiplexer with TRAU frame sync */
/* A E1 sub-channel (de)multiplexer with TRAU frame sync */
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
@ -22,26 +22,77 @@
*/
#include <sys/types.h>
#include <openbsc/linuxlist.h>
#define NR_SUBCH 4
#define TRAU_FRAME_SIZE 40
#define TRAU_FRAME_BITS (TRAU_FRAME_SIZE*8)
struct subch {
/***********************************************************************/
/* DEMULTIPLEXER */
/***********************************************************************/
struct demux_subch {
u_int8_t out_bitbuf[TRAU_FRAME_BITS];
u_int8_t out_idx; /* next bit to be written in out_bitbuf */
};
struct subch_demux {
/* bitmask of currently active subchannels */
u_int8_t chan_activ;
struct subch subch[NR_SUBCH];
/* one demux_subch struct for every subchannel */
struct demux_subch subch[NR_SUBCH];
/* callback to be called once we have received a complete
* frame on a given subchannel */
int (*out_cb)(struct subch_demux *dmx, int ch, u_int8_t *data, int len,
void *);
/* user-provided data, transparently passed to out_cb() */
void *data;
};
/* initialize one demultiplexer instance */
int subch_demux_init(struct subch_demux *dmx);
/* feed 'len' number of muxed bytes into the demultiplexer */
int subch_demux_in(struct subch_demux *dmx, u_int8_t *data, int len);
/* activate decoding/processing for one subchannel */
int subch_demux_activate(struct subch_demux *dmx, int subch);
/* deactivate decoding/processing for one subchannel */
int subch_demux_deactivate(struct subch_demux *dmx, int subch);
/***********************************************************************/
/* MULTIPLEXER */
/***********************************************************************/
/* one element in the tx_queue of a muxer sub-channel */
struct subch_txq_entry {
struct llist_head list;
unsigned int bit_len; /* total number of bits in 'bits' */
unsigned int next_bit; /* next bit to be transmitted */
u_int8_t bits[0]; /* one bit per byte */
};
struct mux_subch {
struct llist_head tx_queue;
};
/* structure representing one instance of the subchannel muxer */
struct subch_mux {
struct mux_subch subch[NR_SUBCH];
};
/* initialize a subchannel muxer instance */
int subchan_mux_init(struct subch_mux *mx);
/* request the output of 'len' multiplexed bytes */
int subchan_mux_out(struct subch_mux *mx, u_int8_t *data, int len);
/* enqueue some data into one sub-channel of the muxer */
int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const u_int8_t *data,
int len);
#endif /* _SUBCH_DEMUX_H */

View File

@ -59,6 +59,7 @@ struct decoded_trau_frame {
int decode_trau_frame(struct decoded_trau_frame *fr, const u_int8_t *trau_bits);
int encode_trau_frame(u_int8_t *trau_bits, const struct decoded_trau_frame *fr);
int trau_frame_up2down(struct decoded_trau_frame *fr);
u_int8_t *trau_idle_frame(void);
#endif /* _TRAU_FRAME_H */

View File

@ -0,0 +1,41 @@
/* 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 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.
*
*/
/* The "TRAU mux map" defines which particular 16kbit sub-slot (in which E1
* timeslot on which E1 interface) should be directly muxed to which other
* sub-slot. Entries in the mux map are always bi-directional.
*
* The idea of all this is to directly switch voice channels in the BSC
* from one phone to another. We do this right now since we don't support
* any external interface for voice channels, and in the future as an
* optimization to routing them externally.
*/
/* map a TRAU mux map entry */
int trau_mux_map(const struct gsm_e1_subslot *src,
const struct gsm_e1_subslot *dst);
/* unmap a TRAU mux map entry */
int trau_mux_unmap(const struct gsm_e1_subslot *ss);
/* 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);

View File

@ -3,10 +3,11 @@ AM_CFLAGS=-Wall
sbin_PROGRAMS = bsc_hack bs11_config
bsc_hack_SOURCES = bsc_hack.c misdn.c abis_rsl.c abis_nm.c gsm_04_08.c gsm_data.c \
bsc_hack_SOURCES = bsc_hack.c abis_rsl.c abis_nm.c gsm_04_08.c gsm_data.c \
gsm_subscriber.c msgb.c select.c chan_alloc.c timer.c debug.c db.c \
gsm_04_11.c telnet_interface.c telnet_parser.l subchan_demux.c \
trau_frame.c paging.c
trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c \
input/misdn.c
bsc_hack_LDADD = -ldl -ldbi
bs11_config_SOURCES = bs11_config.c abis_nm.c gsm_data.c msgb.c debug.c select.c timer.c rs232.c

View File

@ -103,7 +103,7 @@ static int create_objects(struct gsm_bts *bts)
abis_nm_bs11_set_trx1_pw(bts, trx1_password);
sleep(1);
#if 0
cur = tlv_put(cur, NM_ATT_BS11_PASSWORD, 10,
(u_int8_t *)trx1_password);
memcpy(cur, obj_bbsig0_attr, sizeof(obj_bbsig0_attr));
@ -114,7 +114,7 @@ static int create_objects(struct gsm_bts *bts)
sizeof(obj_pa0_attr), obj_pa0_attr);
abis_nm_bs11_set_trx_power(&bts->trx[1], BS11_TRX_POWER_GSM_30mW);
#endif
return 0;
}

View File

@ -45,6 +45,7 @@
#include <openbsc/misdn.h>
#include <openbsc/telnet_interface.h>
#include <openbsc/paging.h>
#include <openbsc/e1_input.h>
/* global pointer to the gsm network data structure */
static struct gsm_network *gsmnet;
@ -654,17 +655,26 @@ static void bootstrap_rsl(struct gsm_bts_trx *trx)
set_system_infos(trx);
}
static void mi_cb(int event, struct gsm_bts *bts)
void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
{
switch (event) {
case EVT_E1_OML_UP:
bootstrap_om(bts);
case EVT_E1_TEI_UP:
switch (type) {
case E1INP_SIGN_OML:
bootstrap_om(trx->bts);
break;
case E1INP_SIGN_RSL:
bootstrap_rsl(trx);
break;
default:
break;
}
break;
case EVT_E1_RSL_UP:
bootstrap_rsl(bts->c0);
case EVT_E1_TEI_DN:
fprintf(stderr, "Lost some E1 TEI link\n");
/* FIXME: deal with TEI or L1 link loss */
break;
default:
/* FIXME: deal with TEI or L1 link loss */
break;
}
}
@ -697,15 +707,14 @@ static int bootstrap_network(void)
bts->paging.channel_allocated = bsc_hack_channel_allocated;
telnet_init(gsmnet, 4242);
if (mi_setup(bts, 0, mi_cb) < 0)
return -EIO;
return 0;
/* E1 mISDN input setup */
return e1_config(bts);
}
static void create_pcap_file(char *file)
{
#if 0
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
int fd = open(file, O_WRONLY|O_TRUNC|O_CREAT, mode);
@ -715,6 +724,9 @@ static void create_pcap_file(char *file)
}
mi_set_pcap_fd(fd);
#else
fprintf(stderr, "PCAP support currently disabled!!\n");
#endif
}
static void print_usage()
@ -815,6 +827,8 @@ static void signal_handler(int signal)
int main(int argc, char **argv)
{
int rc;
/* parse options */
handle_options(argc, argv);
@ -830,7 +844,9 @@ int main(int argc, char **argv)
}
printf("DB: Database prepared.\n");
bootstrap_network();
rc = bootstrap_network();
if (rc < 0)
exit(1);
signal(SIGHUP, &signal_handler);
signal(SIGABRT, &signal_handler);

57
src/e1_config.c Normal file
View File

@ -0,0 +1,57 @@
#include <string.h>
#include <errno.h>
#include <openbsc/gsm_data.h>
#include <openbsc/e1_input.h>
#define SAPI_L2ML 0
#define SAPI_OML 62
#define SAPI_RSL 0 /* 63 ? */
#define TEI_L2ML 127
#define TEI_OML 25
#define TEI_RSL 1
/* do some compiled-in configuration for our BTS/E1 setup */
int e1_config(struct gsm_bts *bts)
{
struct e1inp_line *line;
struct e1inp_ts *sign_ts;
struct e1inp_sign_link *oml_link, *rsl_link;
line = malloc(sizeof(*line));
if (!line)
return -ENOMEM;
memset(line, 0, sizeof(*line));
/* create E1 timeslots for signalling and TRAU frames */
e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN);
e1inp_ts_config(&line->ts[2-1], line, E1INP_TS_TYPE_TRAU);
e1inp_ts_config(&line->ts[3-1], line, E1INP_TS_TYPE_TRAU);
/* create signalling links for TS1 */
sign_ts = &line->ts[1-1];
oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML,
bts->c0, TEI_OML, SAPI_OML);
rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL,
bts->c0, TEI_RSL, SAPI_RSL);
/* create back-links from bts/trx */
bts->oml_link = oml_link;
bts->c0->rsl_link = rsl_link;
#if 0
/* create E1 timeslots for signalling and TRAU frames */
e1inp_ts_config(&line->ts[4-1], line, E1INP_TS_TYPE_TRAU);
e1inp_ts_config(&line->ts[5-1], line, E1INP_TS_TYPE_TRAU);
/* create signalling links for TS1 */
sign_ts = &line->ts[1-1];
rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL,
&bts->trx[1], TEI_RSL+1, SAPI_RSL);
/* create back-links from trx */
bts->trx[1].rsl_link = rsl_link;
#endif
return mi_setup(0, line, NULL);
}

453
src/e1_input.c Normal file
View File

@ -0,0 +1,453 @@
/* OpenBSC Abis interface to E1 */
/* (C) 2008-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 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.
*
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <mISDNif.h>
//#define AF_COMPATIBILITY_FUNC
//#include <compat_af_isdn.h>
#define AF_ISDN 34
#define PF_ISDN AF_ISDN
#include <openbsc/select.h>
#include <openbsc/msgb.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <openbsc/e1_input.h>
#include <openbsc/abis_nm.h>
#include <openbsc/abis_rsl.h>
#include <openbsc/linuxlist.h>
#include <openbsc/subchan_demux.h>
#include <openbsc/trau_mux.h>
#define NUM_E1_TS 32
/* list of all E1 drivers */
static LLIST_HEAD(driver_list);
/* list of all E1 lines */
static LLIST_HEAD(line_list);
#if 0
/*
* pcap writing of the misdn load
* pcap format is from http://wiki.wireshark.org/Development/LibpcapFileFormat
*/
#define DLT_LINUX_LAPD 177
#define PCAP_INPUT 0
#define PCAP_OUTPUT 1
struct pcap_hdr {
u_int32_t magic_number;
u_int16_t version_major;
u_int16_t version_minor;
int32_t thiszone;
u_int32_t sigfigs;
u_int32_t snaplen;
u_int32_t network;
} __attribute__((packed));
struct pcaprec_hdr {
u_int32_t ts_sec;
u_int32_t ts_usec;
u_int32_t incl_len;
u_int32_t orig_len;
} __attribute__((packed));
struct fake_linux_lapd_header {
u_int16_t pkttype;
u_int16_t hatype;
u_int16_t halen;
u_int64_t addr;
int16_t protocol;
} __attribute__((packed));
struct lapd_header {
u_int8_t ea1 : 1;
u_int8_t cr : 1;
u_int8_t sapi : 6;
u_int8_t ea2 : 1;
u_int8_t tei : 7;
u_int8_t control_foo; /* fake UM's ... */
} __attribute__((packed));
static_assert((int)&((struct fake_linux_lapd_header*)NULL)->hatype == 2, hatype_offset);
static_assert((int)&((struct fake_linux_lapd_header*)NULL)->halen == 4, halen_offset);
static_assert((int)&((struct fake_linux_lapd_header*)NULL)->addr == 6, addr_offset);
static_assert((int)&((struct fake_linux_lapd_header*)NULL)->protocol == 14, proto_offset);
static_assert(sizeof(struct fake_linux_lapd_header) == 16, lapd_header_size);
static int pcap_fd = -1;
void mi_set_pcap_fd(int fd)
{
int ret;
struct pcap_hdr header = {
.magic_number = 0xa1b2c3d4,
.version_major = 2,
.version_minor = 4,
.thiszone = 0,
.sigfigs = 0,
.snaplen = 65535,
.network = DLT_LINUX_LAPD,
};
pcap_fd = fd;
ret = write(pcap_fd, &header, sizeof(header));
}
/* This currently only works for the D-Channel */
static void write_pcap_packet(int direction, struct sockaddr_mISDN* addr,
struct msgb *msg) {
if (pcap_fd < 0)
return;
int ret;
time_t cur_time;
struct tm *tm;
struct fake_linux_lapd_header header = {
.pkttype = 4,
.hatype = 0,
.halen = 0,
.addr = direction == PCAP_OUTPUT ? 0x0 : 0x1,
.protocol = ntohs(48),
};
struct lapd_header lapd_header = {
.ea1 = 0,
.cr = direction == PCAP_OUTPUT ? 1 : 0,
.sapi = addr->sapi & 0x3F,
.ea2 = 1,
.tei = addr->tei & 0x7F,
.control_foo = 0x03 /* UI */,
};
struct pcaprec_hdr payload_header = {
.ts_sec = 0,
.ts_usec = 0,
.incl_len = msg->len + sizeof(struct fake_linux_lapd_header)
+ sizeof(struct lapd_header)
- MISDN_HEADER_LEN,
.orig_len = msg->len + sizeof(struct fake_linux_lapd_header)
+ sizeof(struct lapd_header)
- MISDN_HEADER_LEN,
};
cur_time = time(NULL);
tm = localtime(&cur_time);
payload_header.ts_sec = mktime(tm);
ret = write(pcap_fd, &payload_header, sizeof(payload_header));
ret = write(pcap_fd, &header, sizeof(header));
ret = write(pcap_fd, &lapd_header, sizeof(lapd_header));
ret = write(pcap_fd, msg->data + MISDN_HEADER_LEN,
msg->len - MISDN_HEADER_LEN);
}
#endif
/* callback when a TRAU frame was received */
static int subch_cb(struct subch_demux *dmx, int ch, u_int8_t *data, int len,
void *_priv)
{
struct e1inp_ts *e1i_ts = _priv;
struct gsm_e1_subslot src_ss;
src_ss.e1_nr = e1i_ts->line->num;
src_ss.e1_ts = e1i_ts->num;
src_ss.e1_ts_ss = ch;
return trau_mux_input(&src_ss, data, len);
}
int abis_rsl_sendmsg(struct msgb *msg)
{
struct e1inp_sign_link *sign_link;
struct e1inp_driver *e1inp_driver;
msg->l2h = msg->data;
if (!msg->trx || !msg->trx->rsl_link) {
fprintf(stderr, "rsl_sendmsg: msg->trx == NULL\n");
return -EINVAL;
}
sign_link = msg->trx->rsl_link;
msgb_enqueue(&sign_link->tx_list, msg);
/* notify the driver we have something to write */
e1inp_driver = sign_link->ts->line->driver;
e1inp_driver->want_write(sign_link->ts);
return 0;
}
int _abis_nm_sendmsg(struct msgb *msg)
{
struct e1inp_sign_link *sign_link;
struct e1inp_driver *e1inp_driver;
msg->l2h = msg->data;
if (!msg->trx || !msg->trx->bts || !msg->trx->bts->oml_link) {
fprintf(stderr, "rsl_sendmsg: msg->trx == NULL\n");
return -EINVAL;
}
sign_link = msg->trx->bts->oml_link;
msgb_enqueue(&sign_link->tx_list, msg);
/* notify the driver we have something to write */
e1inp_driver = sign_link->ts->line->driver;
e1inp_driver->want_write(sign_link->ts);
return 0;
}
/* Timeslot */
/* configure and initialize one e1inp_ts */
int e1inp_ts_config(struct e1inp_ts *ts, struct e1inp_line *line,
enum e1inp_ts_type type)
{
ts->type = type;
ts->line = line;
switch (type) {
case E1INP_TS_TYPE_SIGN:
INIT_LLIST_HEAD(&ts->sign.sign_links);
break;
case E1INP_TS_TYPE_TRAU:
subchan_mux_init(&ts->trau.mux);
ts->trau.demux.out_cb = subch_cb;
ts->trau.demux.data = ts;
subch_demux_init(&ts->trau.demux);
break;
default:
fprintf(stderr, "unsupported E1 timeslot type %u\n",
ts->type);
return -EINVAL;
}
return 0;
}
static struct e1inp_line *e1inp_line_get(u_int8_t e1_nr)
{
struct e1inp_line *e1i_line;
/* iterate over global list of e1 lines */
llist_for_each_entry(e1i_line, &line_list, list) {
if (e1i_line->num == e1_nr)
return e1i_line;
}
return NULL;
}
static struct e1inp_ts *e1inp_ts_get(u_int8_t e1_nr, u_int8_t ts_nr)
{
struct e1inp_line *e1i_line;
e1i_line = e1inp_line_get(e1_nr);
if (!e1i_line)
return NULL;
return &e1i_line->ts[ts_nr-1];
}
struct subch_mux *e1inp_get_mux(u_int8_t e1_nr, u_int8_t ts_nr)
{
struct e1inp_ts *e1i_ts = e1inp_ts_get(e1_nr, ts_nr);
if (!e1i_ts)
return NULL;
return &e1i_ts->trau.mux;
}
/* Signalling Link */
struct e1inp_sign_link *e1inp_lookup_sign_link(struct e1inp_ts *e1i,
u_int8_t tei, u_int8_t sapi)
{
struct e1inp_sign_link *link;
llist_for_each_entry(link, &e1i->sign.sign_links, list) {
if (link->sapi == sapi && link->tei == tei)
return link;
}
return NULL;
}
/* create a new signalling link in a E1 timeslot */
struct e1inp_sign_link *
e1inp_sign_link_create(struct e1inp_ts *ts, enum e1inp_sign_type type,
struct gsm_bts_trx *trx, u_int8_t tei,
u_int8_t sapi)
{
struct e1inp_sign_link *link;
if (ts->type != E1INP_TS_TYPE_SIGN)
return NULL;
link = malloc(sizeof(*link));
if (!link)
return NULL;
memset(link, 0, sizeof(*link));
link->ts = ts;
link->type = type;
INIT_LLIST_HEAD(&link->tx_list);
link->trx = trx;
link->tei = sapi;
llist_add_tail(&link->list, &ts->sign.sign_links);
return link;
}
/* the E1 driver tells us he has received something on a TS */
int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
u_int8_t tei, u_int8_t sapi)
{
struct e1inp_sign_link *link;
int ret;
switch (ts->type) {
case E1INP_TS_TYPE_SIGN:
/* FIXME: write pcap packet */
/* consult the list of signalling links */
link = e1inp_lookup_sign_link(ts, tei, sapi);
if (!link) {
fprintf(stderr, "didn't find singalling link for "
"tei %d, sapi %d\n", tei, sapi);
return -EINVAL;
}
switch (link->type) {
case E1INP_SIGN_OML:
msg->trx = link->trx;
ret = abis_nm_rcvmsg(msg);
break;
case E1INP_SIGN_RSL:
msg->trx = link->trx;
ret = abis_rsl_rcvmsg(msg);
break;
default:
ret = -EINVAL;
fprintf(stderr, "unknown link type %u\n", link->type);
break;
}
break;
case E1INP_TS_TYPE_TRAU:
ret = subch_demux_in(&ts->trau.demux, msg->data, msg->len);
break;
default:
ret = -EINVAL;
fprintf(stderr, "unknown TS type %u\n", ts->type);
break;
}
return ret;
}
#define TSX_ALLOC_SIZE 4096
/* called by driver if it wants to transmit on a given TS */
struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts,
struct e1inp_sign_link **sign_link)
{
struct e1inp_sign_link *link;
struct msgb *msg = NULL;
int len;
switch (e1i_ts->type) {
case E1INP_TS_TYPE_SIGN:
/* FIXME: implement this round robin */
llist_for_each_entry(link, &e1i_ts->sign.sign_links, list) {
msg = msgb_dequeue(&link->tx_list);
if (msg) {
if (sign_link)
*sign_link = link;
break;
}
}
break;
case E1INP_TS_TYPE_TRAU:
msg = msgb_alloc(TSX_ALLOC_SIZE);
if (!msg)
return NULL;
len = subchan_mux_out(&e1i_ts->trau.mux, msg->data, 40);
msgb_put(msg, 40);
break;
default:
fprintf(stderr, "unsupported E1 TS type %u\n", e1i_ts->type);
return NULL;
}
return msg;
}
/* called by driver in case some kind of link state event */
int e1inp_event(struct e1inp_ts *ts, int evt, u_int8_t tei, u_int8_t sapi)
{
struct e1inp_sign_link *link;
link = e1inp_lookup_sign_link(ts, tei, sapi);
if (!link)
return -EINVAL;
/* FIXME: report further upwards */
return input_event(evt, link->type, link->trx);
}
/* register a driver with the E1 core */
int e1inp_driver_register(struct e1inp_driver *drv)
{
llist_add_tail(&drv->list, &driver_list);
return 0;
}
/* register a line with the E1 core */
int e1inp_line_register(struct e1inp_line *line)
{
int i;
for (i = 0; i < NUM_E1_TS; i++)
line->ts[i].num = i+1;
llist_add_tail(&line->list, &line_list);
return 0;
}

483
src/input/misdn.c Normal file
View File

@ -0,0 +1,483 @@
/* OpenBSC Abis input driver for mISDNuser */
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
*
* All Rights Reserved
*
* 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.
*
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <mISDNif.h>
//#define AF_COMPATIBILITY_FUNC
//#include <compat_af_isdn.h>
#define AF_ISDN 34
#define PF_ISDN AF_ISDN
#include <openbsc/select.h>
#include <openbsc/msgb.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <openbsc/abis_nm.h>
#include <openbsc/abis_rsl.h>
#include <openbsc/subchan_demux.h>
#include <openbsc/e1_input.h>
/* data structure for one E1 interface with A-bis */
struct mi_e1_handle {
/* The mISDN card number of the card we use */
int cardnr;
};
#define TS1_ALLOC_SIZE 300
struct prim_name {
unsigned int prim;
const char *name;
};
const struct prim_name prim_names[] = {
{ DL_ESTABLISH_IND, "DL_ESTABLISH_IND" },
{ DL_ESTABLISH_CNF, "DL_ESTABLISH_CNF" },
{ DL_RELEASE_IND, "DL_RELEASE_IND" },
{ DL_RELEASE_CNF, "DL_RELEASE_CNF" },
{ DL_DATA_IND, "DL_DATA_IND" },
{ DL_UNITDATA_IND, "DL_UNITDATA_IND" },
{ DL_INFORMATION_IND, "DL_INFORMATION_IND" },
{ MPH_ACTIVATE_IND, "MPH_ACTIVATE_IND" },
{ MPH_DEACTIVATE_IND, "MPH_DEACTIVATE_IND" },
};
const char *get_prim_name(unsigned int prim)
{
int i;
for (i = 0; i < ARRAY_SIZE(prim_names); i++) {
if (prim_names[i].prim == prim)
return prim_names[i].name;
}
return "UNKNOWN";
}
static int handle_ts1_read(struct bsc_fd *bfd)
{
struct e1inp_line *line = bfd->data;
unsigned int ts_nr = bfd->priv_nr;
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
struct e1inp_sign_link *link;
struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE);
struct sockaddr_mISDN l2addr;
struct mISDNhead *hh;
socklen_t alen;
int ret;
if (!msg)
return -ENOMEM;
hh = (struct mISDNhead *) msg->data;
alen = sizeof(l2addr);
ret = recvfrom(bfd->fd, msg->data, 300, 0,
(struct sockaddr *) &l2addr, &alen);
if (ret < 0) {
fprintf(stderr, "recvfrom error %s\n", strerror(errno));
return ret;
}
if (alen != sizeof(l2addr))
return -EINVAL;
msgb_put(msg, ret);
DEBUGP(DMI, "alen =%d, dev(%d) channel(%d) sapi(%d) tei(%d)\n",
alen, l2addr.dev, l2addr.channel, l2addr.sapi, l2addr.tei);
DEBUGP(DMI, "<= len = %d, prim(0x%x) id(0x%x)\n",
ret, hh->prim, hh->id);
DEBUGP(DMI, "got %s:\n", get_prim_name(hh->prim));
switch (hh->prim) {
case DL_INFORMATION_IND:
/* mISDN tells us which channel number is allocated for this
* tuple of (SAPI, TEI). */
DEBUGP(DMI, "use channel(%d) sapi(%d) tei(%d) for now\n",
l2addr.channel, l2addr.sapi, l2addr.tei);
link = e1inp_lookup_sign_link(e1i_ts, l2addr.tei, l2addr.sapi);
if (!link) {
DEBUGPC(DMI, "mISDN message for unknown sign_link\n");
free(msg);
return -EINVAL;
}
/* save the channel number in the driver private struct */
link->driver.misdn.channel = l2addr.channel;
break;
case MPH_ACTIVATE_IND:
ret = e1inp_event(e1i_ts, EVT_E1_TEI_UP, l2addr.tei, l2addr.sapi);
break;
case MPH_DEACTIVATE_IND:
ret = e1inp_event(e1i_ts, EVT_E1_TEI_DN, l2addr.tei, l2addr.sapi);
break;
case DL_DATA_IND:
DEBUGP(DMI, "got DL_DATA_IND\n");
msg->l2h = msg->data + MISDN_HEADER_LEN;
if (debug_mask & DMI) {
fprintf(stdout, "RX: ");
hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN);
}
ret = e1inp_rx_ts(e1i_ts, msg, l2addr.tei, l2addr.sapi);
break;
default:
break;
}
return ret;
}
static int handle_ts1_write(struct bsc_fd *bfd)
{
struct e1inp_line *line = bfd->data;
unsigned int ts_nr = bfd->priv_nr;
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
struct e1inp_sign_link *sign_link;
struct sockaddr_mISDN sa;
struct msgb *msg;
struct mISDNhead *hh;
u_int8_t *l2_data;
int ret;
/* get the next msg for this timeslot */
msg = e1inp_tx_ts(e1i_ts, &sign_link);
if (!msg) {
bfd->when &= ~BSC_FD_WRITE;
return 0;
}
l2_data = msg->data;
/* prepend the mISDNhead */
hh = (struct mISDNhead *) msgb_push(msg, sizeof(*hh));
hh->prim = DL_DATA_REQ;
if (debug_mask & DMI) {
fprintf(stdout, "TX: ");
hexdump(l2_data, msg->len - MISDN_HEADER_LEN);
}
/* construct the sockaddr */
sa.family = AF_ISDN;
sa.sapi = sign_link->sapi;
sa.dev = sign_link->tei;
sa.channel = sign_link->driver.misdn.channel;
ret = sendto(bfd->fd, msg->data, msg->len, 0,
(struct sockaddr *)&sa, sizeof(sa));
msgb_free(msg);
/* FIXME: this has to go */
usleep(100000);
return ret;
}
#define TSX_ALLOC_SIZE 4096
/* FIXME: read from a B channel TS */
static int handle_tsX_read(struct bsc_fd *bfd)
{
struct e1inp_line *line = bfd->data;
unsigned int ts_nr = bfd->priv_nr;
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
struct msgb *msg = msgb_alloc(TSX_ALLOC_SIZE);
struct mISDNhead *hh;
int ret;
if (!msg)
return -ENOMEM;
hh = (struct mISDNhead *) msg->data;
ret = recv(bfd->fd, msg->data, TSX_ALLOC_SIZE, 0);
if (ret < 0) {
fprintf(stderr, "recvfrom error %s\n", strerror(errno));
return ret;
}
msgb_put(msg, ret);
DEBUGP(DMIB, "<= BCHAN len = %d, prim(0x%x) id(0x%x): %s\n",
ret, hh->prim, hh->id, get_prim_name(hh->prim));
switch (hh->prim) {
case PH_DATA_IND:
msg->l2h = msg->data + MISDN_HEADER_LEN;
if (debug_mask & DMIB) {
fprintf(stdout, "BCHAN RX: ");
hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN);
}
ret = e1inp_rx_ts(e1i_ts, msg, 0, 0);
break;
default:
break;
}
/* FIXME: why do we free signalling msgs in the caller, and trau not? */
msgb_free(msg);
return ret;
}
#define BCHAN_TX_GRAN 40
/* write to a B channel TS */
static int handle_tsX_write(struct bsc_fd *bfd)
{
struct e1inp_line *line = bfd->data;
unsigned int ts_nr = bfd->priv_nr;
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
struct mISDNhead *hh;
u_int8_t tx_buf[BCHAN_TX_GRAN + sizeof(*hh)];
struct subch_mux *mx = &e1i_ts->trau.mux;
int ret;
hh = (struct mISDNhead *) tx_buf;
hh->prim = PH_DATA_REQ;
subchan_mux_out(mx, tx_buf+sizeof(*hh), BCHAN_TX_GRAN);
ret = send(bfd->fd, tx_buf, sizeof(*hh) + BCHAN_TX_GRAN, 0);
if (ret < sizeof(*hh) + BCHAN_TX_GRAN)
DEBUGP(DMIB, "send returns %d instead of %u\n", ret,
sizeof(*hh) + BCHAN_TX_GRAN);
return ret;
}
/* callback from select.c in case one of the fd's can be read/written */
static int misdn_fd_cb(struct bsc_fd *bfd, unsigned int what)
{
struct e1inp_line *line = bfd->data;
unsigned int ts_nr = bfd->priv_nr;
unsigned int idx = ts_nr-1;
struct e1inp_ts *e1i_ts = &line->ts[idx];
int rc = 0;
switch (e1i_ts->type) {
case E1INP_TS_TYPE_SIGN:
if (what & BSC_FD_READ)
rc = handle_ts1_read(bfd);
if (what & BSC_FD_WRITE)
rc = handle_ts1_write(bfd);
break;
case E1INP_TS_TYPE_TRAU:
if (what & BSC_FD_READ)
rc = handle_tsX_read(bfd);
if (what & BSC_FD_WRITE)
rc = handle_tsX_write(bfd);
break;
default:
fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type);
break;
}
return rc;
}
static int activate_bchan(struct e1inp_line *line, int ts, int act)
{
struct mISDNhead hh;
int ret;
unsigned int idx = ts-1;
struct e1inp_ts *e1i_ts = &line->ts[idx];
struct bsc_fd *bfd = &e1i_ts->driver.misdn.fd;
fprintf(stdout, "activate bchan\n");
if (act)
hh.prim = PH_ACTIVATE_REQ;
else
hh.prim = PH_DEACTIVATE_REQ;
hh.id = MISDN_ID_ANY;
ret = sendto(bfd->fd, &hh, sizeof(hh), 0, NULL, 0);
if (ret < 0) {
fprintf(stdout, "could not send ACTIVATE_RQ %s\n",
strerror(errno));
}
return ret;
}
static int ts_want_write(struct e1inp_ts *e1i_ts)
{
e1i_ts->driver.misdn.fd.when |= BSC_FD_WRITE;
return 0;
}
struct e1inp_driver misdn_driver = {
.name = "mISDNuser",
.want_write = ts_want_write,
};
static int mi_e1_setup(struct e1inp_line *line)
{
struct mi_e1_handle *e1h = line->driver_data;
int ts, ret;
/* TS0 is CRC4, don't need any fd for it */
for (ts = 1; ts < NUM_E1_TS; ts++) {
unsigned int idx = ts-1;
struct e1inp_ts *e1i_ts = &line->ts[idx];
struct bsc_fd *bfd = &e1i_ts->driver.misdn.fd;
struct sockaddr_mISDN addr;
bfd->data = line;
bfd->priv_nr = ts;
bfd->cb = misdn_fd_cb;
switch (e1i_ts->type) {
case E1INP_TS_TYPE_NONE:
continue;
break;
case E1INP_TS_TYPE_SIGN:
bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_NT);
bfd->when = BSC_FD_READ;
break;
case E1INP_TS_TYPE_TRAU:
bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_B_RAW);
bfd->when = BSC_FD_READ | BSC_FD_WRITE;
break;
}
if (bfd->fd < 0) {
fprintf(stderr, "could not open socket %s\n",
strerror(errno));
return bfd->fd;
}
memset(&addr, 0, sizeof(addr));
addr.family = AF_ISDN;
addr.dev = e1h->cardnr;
switch (e1i_ts->type) {
case E1INP_TS_TYPE_SIGN:
addr.channel = 0;
/* SAPI not supported yet in kernel */
//addr.sapi = e1inp_ts->sign.sapi;
addr.sapi = 0;
addr.tei = GROUP_TEI;
break;
case E1INP_TS_TYPE_TRAU:
addr.channel = ts;
break;
default:
DEBUGP(DMI, "unsupported E1 TS type: %u\n",
e1i_ts->type);
break;
}
ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
if (ret < 0) {
fprintf(stderr, "could not bind l2 socket %s\n",
strerror(errno));
return -EIO;
}
/* FIXME: only activate B-Channels once we start to
* use them to conserve CPU power */
if (e1i_ts->type == E1INP_TS_TYPE_TRAU)
activate_bchan(line, ts, 1);
ret = bsc_register_fd(bfd);
if (ret < 0) {
fprintf(stderr, "could not register FD: %s\n",
strerror(ret));
return ret;
}
}
return 0;
}
int mi_setup(int cardnr, struct e1inp_line *line)
{
struct mi_e1_handle *e1h;
int sk, ret, cnt;
struct mISDN_devinfo devinfo;
/* register the driver with the core */
/* FIXME: do this in the plugin initializer function */
ret = e1inp_driver_register(&misdn_driver);
if (ret)
return ret;
/* create the actual line instance */
/* FIXME: do this independent of driver registration */
e1h = malloc(sizeof(*e1h));
memset(e1h, 0, sizeof(*e1h));
e1h->cardnr = cardnr;
line->driver = &misdn_driver;
line->driver_data = e1h;
/* open the ISDN card device */
sk = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE);
if (sk < 0) {
fprintf(stderr, "could not open socket %s\n", strerror(errno));
return sk;
}
ret = ioctl(sk, IMGETCOUNT, &cnt);
if (ret) {
fprintf(stderr, "error getting interf count: %s\n",
strerror(errno));
close(sk);
return -ENODEV;
}
//DEBUGP(DMI,"%d device%s found\n", cnt, (cnt==1)?"":"s");
printf("%d device%s found\n", cnt, (cnt==1)?"":"s");
#if 1
devinfo.id = e1h->cardnr;
ret = ioctl(sk, IMGETDEVINFO, &devinfo);
if (ret < 0) {
fprintf(stdout, "error getting info for device %d: %s\n",
e1h->cardnr, strerror(errno));
return -ENODEV;
}
fprintf(stdout, " id: %d\n", devinfo.id);
fprintf(stdout, " Dprotocols: %08x\n", devinfo.Dprotocols);
fprintf(stdout, " Bprotocols: %08x\n", devinfo.Bprotocols);
fprintf(stdout, " protocol: %d\n", devinfo.protocol);
fprintf(stdout, " nrbchan: %d\n", devinfo.nrbchan);
fprintf(stdout, " name: %s\n", devinfo.name);
#endif
ret = mi_e1_setup(line);
if (ret)
return ret;
return e1inp_line_register(line);
}

View File

@ -1,4 +1,4 @@
/* A E1 sub-channel demultiplexer with TRAU frame sync */
/* A E1 sub-channel (de)multiplexer with TRAU frame sync */
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
@ -26,8 +26,9 @@
#include <errno.h>
#include <openbsc/subchan_demux.h>
#include <openbsc/trau_frame.h>
static inline void append_bit(struct subch *sch, u_int8_t bit)
static inline void append_bit(struct demux_subch *sch, u_int8_t bit)
{
sch->out_bitbuf[sch->out_idx++] = bit;
}
@ -37,7 +38,7 @@ 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 subch *sch)
static int sync_hdr_complete(struct demux_subch *sch)
{
int rc;
int bits_at_end = 0;
@ -62,7 +63,7 @@ static int sync_hdr_complete(struct subch *sch)
}
/* resynchronize to current location */
static void resync_to_here(struct subch *sch)
static void resync_to_here(struct demux_subch *sch)
{
#if 0
u_int8_t tmp[TRAU_FRAME_BITS];
@ -98,7 +99,7 @@ int subch_demux_init(struct subch_demux *dmx)
dmx->chan_activ = 0;
for (i = 0; i < NR_SUBCH; i++) {
struct subch *sch = &dmx->subch[i];
struct demux_subch *sch = &dmx->subch[i];
sch->out_idx = 0;
memset(sch->out_bitbuf, 0xff, sizeof(sch->out_bitbuf));
}
@ -119,7 +120,7 @@ int subch_demux_in(struct subch_demux *dmx, u_int8_t *data, int len)
u_int8_t inbyte = data[i];
for (c = 0; c < NR_SUBCH; c++) {
struct subch *sch = &dmx->subch[c];
struct demux_subch *sch = &dmx->subch[c];
u_int8_t bit;
/* ignore inactive subchannels */
@ -177,3 +178,134 @@ int subch_demux_deactivate(struct subch_demux *dmx, int subch)
dmx->chan_activ &= ~(1 << subch);
return 0;
}
/* MULTIPLEXER */
static int alloc_add_idle_frame(struct subch_mux *mx, int sch_nr)
{
/* FIXME: 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, 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_requested);
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);
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(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);
if (!rc)
*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;
}
/* 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];
struct subch_txq_entry *tqe = malloc(sizeof(*tqe) + len);
if (!tqe)
return -ENOMEM;
memset(tqe, 0, sizeof(*tqe));
tqe->bit_len = len;
memcpy(tqe->bits, data, len);
llist_add(&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;
}

View File

@ -26,6 +26,7 @@
#include <errno.h>
#include <openbsc/trau_frame.h>
#include <openbsc/subchan_demux.h>
static u_int32_t get_bits(const u_int8_t *bitbuf, int offset, int num)
{
@ -192,6 +193,9 @@ static void encode_fr(u_int8_t *trau_bits, const struct decoded_trau_frame *fr)
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);
}
@ -230,3 +234,21 @@ int encode_trau_frame(u_int8_t *trau_bits, const struct decoded_trau_frame *fr)
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;
}

109
src/trau_mux.c Normal file
View File

@ -0,0 +1,109 @@
/* 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 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.
*
*/
#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>
struct map_entry {
struct llist_head list;
struct gsm_e1_subslot src, dst;
};
static LLIST_HEAD(ss_map);
/* 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 = malloc(sizeof(*me));
if (!me)
return -ENOMEM;
memcpy(&me->src, src, sizeof(me->src));
memcpy(&me->dst, dst, sizeof(me->dst));
llist_add(&me->list, &ss_map);
return 0;
}
/* unmap one particular subslot from another subslot */
int trau_mux_unmap(const struct gsm_e1_subslot *ss)
{
struct map_entry *me, *me2;
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;
}
}
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;
}
/* 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;
if (!dst_e1_ss)
return -EINVAL;
mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
if (!mx)
return -EINVAL;
/* decode TRAU, change it to downlink, re-encode */
decode_trau_frame(&tf, trau_bits);
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);
}