diff --git a/include/openbsc/abis_nm.h b/include/openbsc/abis_nm.h index 2df941e6e..e75f108b2 100644 --- a/include/openbsc/abis_nm.h +++ b/include/openbsc/abis_nm.h @@ -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 */ diff --git a/include/openbsc/e1_input.h b/include/openbsc/e1_input.h new file mode 100644 index 000000000..29c29d081 --- /dev/null +++ b/include/openbsc/e1_input.h @@ -0,0 +1,137 @@ +#ifndef _E1_INPUT_H +#define _E1_INPUT_H + +#include + +#include +#include +#include +#include +#include + +#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 */ diff --git a/include/openbsc/gsm_data.h b/include/openbsc/gsm_data.h index 75b3db516..b96ec402d 100644 --- a/include/openbsc/gsm_data.h +++ b/include/openbsc/gsm_data.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 diff --git a/include/openbsc/subchan_demux.h b/include/openbsc/subchan_demux.h index 373cf4a30..23c903204 100644 --- a/include/openbsc/subchan_demux.h +++ b/include/openbsc/subchan_demux.h @@ -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 * All Rights Reserved @@ -22,26 +22,77 @@ */ #include +#include #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 */ diff --git a/include/openbsc/trau_frame.h b/include/openbsc/trau_frame.h index 7f0a2e69b..5923d4ac1 100644 --- a/include/openbsc/trau_frame.h +++ b/include/openbsc/trau_frame.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 */ diff --git a/include/openbsc/trau_mux.h b/include/openbsc/trau_mux.h new file mode 100644 index 000000000..43836dbc5 --- /dev/null +++ b/include/openbsc/trau_mux.h @@ -0,0 +1,41 @@ +/* Simple TRAU frame reflector to route voice calls */ + +/* (C) 2009 by Harald Welte + * 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); diff --git a/src/Makefile.am b/src/Makefile.am index 53b290371..ae5715cb1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/bs11_config.c b/src/bs11_config.c index 47f557803..e623ade16 100644 --- a/src/bs11_config.c +++ b/src/bs11_config.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; } diff --git a/src/bsc_hack.c b/src/bsc_hack.c index 7fa12e79c..1ff10750b 100644 --- a/src/bsc_hack.c +++ b/src/bsc_hack.c @@ -45,6 +45,7 @@ #include #include #include +#include /* 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); diff --git a/src/e1_config.c b/src/e1_config.c new file mode 100644 index 000000000..cf3a94b09 --- /dev/null +++ b/src/e1_config.c @@ -0,0 +1,57 @@ +#include +#include + +#include +#include + +#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); +} diff --git a/src/e1_input.c b/src/e1_input.c new file mode 100644 index 000000000..8ae2d1c43 --- /dev/null +++ b/src/e1_input.c @@ -0,0 +1,453 @@ +/* OpenBSC Abis interface to E1 */ + +/* (C) 2008-2009 by Harald Welte + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define AF_COMPATIBILITY_FUNC +//#include +#define AF_ISDN 34 +#define PF_ISDN AF_ISDN + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/input/misdn.c b/src/input/misdn.c new file mode 100644 index 000000000..8201293c7 --- /dev/null +++ b/src/input/misdn.c @@ -0,0 +1,483 @@ +/* OpenBSC Abis input driver for mISDNuser */ + +/* (C) 2008-2009 by Harald Welte + * (C) 2009 by Holger Hans Peter Freyther + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define AF_COMPATIBILITY_FUNC +//#include +#define AF_ISDN 34 +#define PF_ISDN AF_ISDN + +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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); +} diff --git a/src/subchan_demux.c b/src/subchan_demux.c index da1b09072..78ba2dae2 100644 --- a/src/subchan_demux.c +++ b/src/subchan_demux.c @@ -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 * All Rights Reserved @@ -26,8 +26,9 @@ #include #include +#include -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; +} diff --git a/src/trau_frame.c b/src/trau_frame.c index 9d7e517ff..4b02c7bb2 100644 --- a/src/trau_frame.c +++ b/src/trau_frame.c @@ -26,6 +26,7 @@ #include #include +#include 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; +} diff --git a/src/trau_mux.c b/src/trau_mux.c new file mode 100644 index 000000000..753d01f95 --- /dev/null +++ b/src/trau_mux.c @@ -0,0 +1,109 @@ +/* Simple TRAU frame reflector to route voice calls */ + +/* (C) 2009 by Harald Welte + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include + +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); +}