* 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:
parent
0b4c34e3ca
commit
1fa60c8ab0
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue