reworked MNCC codebase
This is Harald's reworked MNCC base, slowly heading towards integration into master. The key changes are: * provide much more structure to the data in gsm_mncc * encode_* and decode_* functions now take a structure rather than tons of individual arguments (whose order nobody can remember) * make sure we don't have copies of the same code everywhere by introducing mncc_set_cause() and mncc_release_ind() * save horizontal screen space if possible * make sure we break lines > 80 characters
This commit is contained in:
parent
ec44e1ff41
commit
4bfdfe7f70
|
@ -540,10 +540,59 @@ struct gsm_meas_rep {
|
|||
void gsm48_parse_meas_rep(struct gsm_meas_rep *rep, const u_int8_t *data,
|
||||
int len);
|
||||
|
||||
enum chreq_type {
|
||||
CHREQ_T_EMERG_CALL,
|
||||
CHREQ_T_CALL_REEST_TCH_F,
|
||||
CHREQ_T_CALL_REEST_TCH_H,
|
||||
CHREQ_T_CALL_REEST_TCH_H_DBL,
|
||||
CHREQ_T_SDCCH,
|
||||
CHREQ_T_TCH_F,
|
||||
CHREQ_T_VOICE_CALL_TCH_H,
|
||||
CHREQ_T_DATA_CALL_TCH_H,
|
||||
CHREQ_T_LOCATION_UPD,
|
||||
CHREQ_T_PAG_R_ANY,
|
||||
CHREQ_T_PAG_R_TCH_F,
|
||||
CHREQ_T_PAG_R_TCH_FH,
|
||||
};
|
||||
|
||||
/* Chapter 11.3 */
|
||||
#define GSM48_T301 180, 0
|
||||
#define GSM48_T303 30, 0
|
||||
#define GSM48_T305 30, 0
|
||||
#define GSM48_T306 30, 0
|
||||
#define GSM48_T308 10, 0
|
||||
#define GSM48_T310 180, 0
|
||||
#define GSM48_T313 30, 0
|
||||
#define GSM48_T323 30, 0
|
||||
#define GSM48_T331 30, 0
|
||||
#define GSM48_T333 30, 0
|
||||
#define GSM48_T334 25, 0 /* min 15 */
|
||||
#define GSM48_T338 30, 0
|
||||
|
||||
/* Chapter 5.1.2.2 */
|
||||
#define GSM_CSTATE_NULL 0
|
||||
#define GSM_CSTATE_INITIATED 1
|
||||
#define GSM_CSTATE_MO_CALL_PROC 3
|
||||
#define GSM_CSTATE_CALL_DELIVERED 4
|
||||
#define GSM_CSTATE_CALL_PRESENT 6
|
||||
#define GSM_CSTATE_CALL_RECEIVED 7
|
||||
#define GSM_CSTATE_CONNECT_REQUEST 8
|
||||
#define GSM_CSTATE_MO_TERM_CALL_CONF 9
|
||||
#define GSM_CSTATE_ACTIVE 10
|
||||
#define GSM_CSTATE_DISCONNECT_REQ 12
|
||||
#define GSM_CSTATE_DISCONNECT_IND 12
|
||||
#define GSM_CSTATE_RELEASE_REQ 19
|
||||
#define GSM_CSTATE_MO_ORIG_MODIFY 26
|
||||
#define GSM_CSTATE_MO_TERM_MODIFY 27
|
||||
#define GSM_CSTATE_CONNECT_IND 28
|
||||
|
||||
#define SBIT(a) (1 << a)
|
||||
#define ALL_STATES 0xffffffff
|
||||
|
||||
struct msgb;
|
||||
struct gsm_bts;
|
||||
struct gsm_subscriber;
|
||||
struct gsm_network;
|
||||
|
||||
/* config options controlling the behaviour of the lower leves */
|
||||
void gsm0408_allow_everyone(int allow);
|
||||
|
@ -552,7 +601,6 @@ void gsm0408_set_reject_cause(int cause);
|
|||
int gsm0408_rcvmsg(struct msgb *msg);
|
||||
void gsm0408_generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc,
|
||||
u_int16_t mnc, u_int16_t lac);
|
||||
int gsm48_cc_tx_setup(struct gsm_lchan *lchan, struct gsm_subscriber *calling);
|
||||
enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra);
|
||||
enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra);
|
||||
|
||||
|
@ -563,6 +611,10 @@ int generate_mid_from_tmsi(u_int8_t *buf, u_int32_t tmsi);
|
|||
|
||||
int gsm48_send_rr_release(struct gsm_lchan *lchan);
|
||||
|
||||
int bsc_upqueue(struct gsm_network *net);
|
||||
|
||||
int mncc_send(struct gsm_network *net, int msg_type, void *arg);
|
||||
|
||||
/* convert a ASCII phone number to call-control BCD */
|
||||
int encode_bcd_number(u_int8_t *bcd_lv, u_int8_t max_len,
|
||||
int h_len, const char *input);
|
||||
|
|
|
@ -3,8 +3,37 @@
|
|||
|
||||
#include <sys/types.h>
|
||||
|
||||
enum gsm_phys_chan_config {
|
||||
GSM_PCHAN_NONE,
|
||||
GSM_PCHAN_CCCH,
|
||||
GSM_PCHAN_CCCH_SDCCH4,
|
||||
GSM_PCHAN_TCH_F,
|
||||
GSM_PCHAN_TCH_H,
|
||||
GSM_PCHAN_SDCCH8_SACCH8C,
|
||||
GSM_PCHAN_UNKNOWN,
|
||||
};
|
||||
|
||||
enum gsm_chan_t {
|
||||
GSM_LCHAN_NONE,
|
||||
GSM_LCHAN_SDCCH,
|
||||
GSM_LCHAN_TCH_F,
|
||||
GSM_LCHAN_TCH_H,
|
||||
GSM_LCHAN_UNKNOWN,
|
||||
};
|
||||
|
||||
|
||||
/* Channel Request reason */
|
||||
enum gsm_chreq_reason_t {
|
||||
GSM_CHREQ_REASON_EMERG,
|
||||
GSM_CHREQ_REASON_PAG,
|
||||
GSM_CHREQ_REASON_CALL,
|
||||
GSM_CHREQ_REASON_LOCATION_UPD,
|
||||
GSM_CHREQ_REASON_OTHER,
|
||||
};
|
||||
|
||||
#include <openbsc/timer.h>
|
||||
#include <openbsc/gsm_04_08.h>
|
||||
#include <openbsc/mncc.h>
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
|
@ -59,66 +88,41 @@ struct gsm_bts_link {
|
|||
struct gsm_bts *bts;
|
||||
};
|
||||
|
||||
enum gsm_call_type {
|
||||
GSM_CT_NONE,
|
||||
GSM_CT_MO,
|
||||
GSM_CT_MT,
|
||||
};
|
||||
|
||||
enum gsm_call_state {
|
||||
GSM_CSTATE_NULL,
|
||||
GSM_CSTATE_INITIATED,
|
||||
GSM_CSTATE_ACTIVE,
|
||||
GSM_CSTATE_RELEASE_REQ,
|
||||
};
|
||||
|
||||
struct gsm_lchan;
|
||||
struct gsm_subscriber;
|
||||
struct gsm_mncc;
|
||||
|
||||
/* One end of a call */
|
||||
struct gsm_call {
|
||||
enum gsm_call_type type;
|
||||
enum gsm_call_state state;
|
||||
u_int8_t transaction_id; /* 10.3.2 */
|
||||
/* One transaction */
|
||||
struct gsm_trans {
|
||||
/* Entry in list of all transactions */
|
||||
struct llist_head entry;
|
||||
|
||||
/* the 'local' channel */
|
||||
struct gsm_lchan *local_lchan;
|
||||
/* the 'remote' channel */
|
||||
struct gsm_lchan *remote_lchan;
|
||||
/* Network */
|
||||
struct gsm_network *network;
|
||||
|
||||
/* the 'remote' subscriber */
|
||||
struct gsm_subscriber *called_subscr;
|
||||
/* The current transaction ID */
|
||||
u_int8_t transaction_id;
|
||||
|
||||
/* The LCHAN that we're part of */
|
||||
struct gsm_lchan *lchan;
|
||||
|
||||
/* To whom we are allocated at the moment */
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
/* reference */
|
||||
u_int32_t callref;
|
||||
|
||||
/* current call state */
|
||||
int state;
|
||||
|
||||
/* current timer and message queue */
|
||||
int Tcurrent; /* current CC timer */
|
||||
int T308_second; /* used to send release again */
|
||||
struct timer_list cc_timer;
|
||||
struct gsm_mncc cc_msg; /* stores setup/disconnect/release message */
|
||||
};
|
||||
|
||||
|
||||
enum gsm_phys_chan_config {
|
||||
GSM_PCHAN_NONE,
|
||||
GSM_PCHAN_CCCH,
|
||||
GSM_PCHAN_CCCH_SDCCH4,
|
||||
GSM_PCHAN_TCH_F,
|
||||
GSM_PCHAN_TCH_H,
|
||||
GSM_PCHAN_SDCCH8_SACCH8C,
|
||||
GSM_PCHAN_UNKNOWN,
|
||||
};
|
||||
|
||||
enum gsm_chan_t {
|
||||
GSM_LCHAN_NONE,
|
||||
GSM_LCHAN_SDCCH,
|
||||
GSM_LCHAN_TCH_F,
|
||||
GSM_LCHAN_TCH_H,
|
||||
GSM_LCHAN_UNKNOWN,
|
||||
};
|
||||
|
||||
|
||||
/* Channel Request reason */
|
||||
enum gsm_chreq_reason_t {
|
||||
GSM_CHREQ_REASON_EMERG,
|
||||
GSM_CHREQ_REASON_PAG,
|
||||
GSM_CHREQ_REASON_CALL,
|
||||
GSM_CHREQ_REASON_LOCATION_UPD,
|
||||
GSM_CHREQ_REASON_OTHER,
|
||||
};
|
||||
|
||||
/* Network Management State */
|
||||
struct gsm_nm_state {
|
||||
u_int8_t operational;
|
||||
|
@ -162,12 +166,6 @@ struct gsm_lchan {
|
|||
/* Timer started to release the channel */
|
||||
struct timer_list release_timer;
|
||||
|
||||
/* local end of a call, if any */
|
||||
struct gsm_call call;
|
||||
|
||||
/* temporary user data, to be removed... and merged into gsm_call */
|
||||
void *user_data;
|
||||
|
||||
/*
|
||||
* Operations that have a state and might be pending
|
||||
*/
|
||||
|
@ -355,6 +353,11 @@ struct gsm_network {
|
|||
char *name_long;
|
||||
char *name_short;
|
||||
|
||||
/* layer 4 */
|
||||
int (*mncc_recv) (struct gsm_network *net, int msg_type, void *arg);
|
||||
struct llist_head upqueue;
|
||||
struct llist_head trans_list;
|
||||
|
||||
unsigned int num_bts;
|
||||
/* private lists */
|
||||
struct gsm_bts bts[GSM_MAX_BTS+1];
|
||||
|
@ -372,7 +375,8 @@ struct gsm_sms {
|
|||
};
|
||||
|
||||
struct gsm_network *gsm_network_init(unsigned int num_bts, enum gsm_bts_type bts_type,
|
||||
u_int16_t country_code, u_int16_t network_code);
|
||||
u_int16_t country_code, u_int16_t network_code,
|
||||
int (*mncc_recv)(struct gsm_network *, int, void *));
|
||||
|
||||
const char *gsm_pchan_name(enum gsm_phys_chan_config c);
|
||||
const char *gsm_lchan_name(enum gsm_chan_t c);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#define GSM_EXTENSION_LENGTH 128
|
||||
|
||||
struct gsm_subscriber {
|
||||
struct gsm_network *net;
|
||||
long long unsigned int id;
|
||||
char imsi[GSM_IMSI_LENGTH];
|
||||
char tmsi[GSM_TMSI_LENGTH];
|
||||
|
|
|
@ -0,0 +1,210 @@
|
|||
/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface
|
||||
* 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
|
||||
|
||||
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2008, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _MNCC_H
|
||||
#define _MNCC_H
|
||||
|
||||
#include <openbsc/linuxlist.h>
|
||||
|
||||
/* One end of a call */
|
||||
struct gsm_call {
|
||||
struct llist_head entry;
|
||||
|
||||
/* network handle */
|
||||
void *net;
|
||||
|
||||
/* the 'local' transaction */
|
||||
u_int32_t callref;
|
||||
/* the 'remote' transaction */
|
||||
u_int32_t remote_ref;
|
||||
};
|
||||
|
||||
#define MNCC_SETUP_REQ 0x0101
|
||||
#define MNCC_SETUP_IND 0x0102
|
||||
#define MNCC_SETUP_RSP 0x0103
|
||||
#define MNCC_SETUP_CNF 0x0104
|
||||
#define MNCC_SETUP_COMPL_REQ 0x0105
|
||||
#define MNCC_SETUP_COMPL_IND 0x0106
|
||||
/* MNCC_REJ_* is perfomed via MNCC_REL_* */
|
||||
#define MNCC_CALL_CONF_IND 0x0107
|
||||
#define MNCC_CALL_PROC_REQ 0x0108
|
||||
#define MNCC_PROGRESS_REQ 0x0109
|
||||
#define MNCC_ALERT_REQ 0x010a
|
||||
#define MNCC_ALERT_IND 0x010b
|
||||
#define MNCC_NOTIFY_REQ 0x010c
|
||||
#define MNCC_NOTIFY_IND 0x010d
|
||||
#define MNCC_DISC_REQ 0x010e
|
||||
#define MNCC_DISC_IND 0x010f
|
||||
#define MNCC_REL_REQ 0x0110
|
||||
#define MNCC_REL_IND 0x0111
|
||||
#define MNCC_REL_CNF 0x0112
|
||||
#define MNCC_FACILITY_REQ 0x0113
|
||||
#define MNCC_FACILITY_IND 0x0114
|
||||
#define MNCC_START_DTMF_IND 0x0115
|
||||
#define MNCC_START_DTMF_RSP 0x0116
|
||||
#define MNCC_START_DTMF_REJ 0x0117
|
||||
#define MNCC_STOP_DTMF_IND 0x0118
|
||||
#define MNCC_STOP_DTMF_RSP 0x0119
|
||||
#define MNCC_MODIFY_REQ 0x011a
|
||||
#define MNCC_MODIFY_IND 0x011b
|
||||
#define MNCC_MODIFY_RSP 0x011c
|
||||
#define MNCC_MODIFY_CNF 0x011d
|
||||
#define MNCC_MODIFY_REJ 0x011e
|
||||
#define MNCC_HOLD_IND 0x011f
|
||||
#define MNCC_HOLD_CNF 0x0120
|
||||
#define MNCC_HOLD_REJ 0x0121
|
||||
#define MNCC_RETRIEVE_IND 0x0122
|
||||
#define MNCC_RETRIEVE_CNF 0x0123
|
||||
#define MNCC_RETRIEVE_REJ 0x0124
|
||||
#define MNCC_USERINFO_REQ 0x0125
|
||||
#define MNCC_USERINFO_IND 0x0126
|
||||
#define MNCC_REJ_REQ 0x0127
|
||||
#define MNCC_REJ_IND 0x0128
|
||||
|
||||
#define MNCC_BRIDGE 0x0200
|
||||
#define MNCC_FRAME_RECV 0x0201
|
||||
#define MNCC_FRAME_DROP 0x0202
|
||||
#define MNCC_LCHAN_MODIFY 0x0203
|
||||
|
||||
#define GSM_TRAU_FRAME 0x0300
|
||||
|
||||
#define GSM_MAX_FACILITY 128
|
||||
#define GSM_MAX_SSVERSION 128
|
||||
#define GSM_MAX_USERUSER 128
|
||||
|
||||
#define MNCC_F_BEARER_CAP 0x0001
|
||||
#define MNCC_F_CALLED 0x0002
|
||||
#define MNCC_F_CALLING 0x0004
|
||||
#define MNCC_F_REDIRECTING 0x0008
|
||||
#define MNCC_F_CONNECTED 0x0010
|
||||
#define MNCC_F_CAUSE 0x0020
|
||||
#define MNCC_F_USERUSER 0x0040
|
||||
#define MNCC_F_PROGRESS 0x0080
|
||||
#define MNCC_F_EMERGENCY 0x0100
|
||||
#define MNCC_F_FACILITY 0x0200
|
||||
#define MNCC_F_SSVERSION 0x0400
|
||||
#define MNCC_F_CCCAP 0x0800
|
||||
#define MNCC_F_KEYPAD 0x1000
|
||||
#define MNCC_F_SIGNAL 0x2000
|
||||
|
||||
struct gsm_mncc_bearer_cap {
|
||||
int transfer;
|
||||
int mode;
|
||||
int coding;
|
||||
int radio;
|
||||
int speech_ctm;
|
||||
int speech_ver[8];
|
||||
};
|
||||
|
||||
struct gsm_mncc_number {
|
||||
int type;
|
||||
int plan;
|
||||
int present;
|
||||
int screen;
|
||||
char number[33];
|
||||
};
|
||||
|
||||
struct gsm_mncc_cause {
|
||||
int location;
|
||||
int coding;
|
||||
int rec;
|
||||
int rec_val;
|
||||
int value;
|
||||
int diag_len;
|
||||
char diag[32];
|
||||
};
|
||||
|
||||
struct gsm_mncc_useruser {
|
||||
int proto;
|
||||
char info[GSM_MAX_USERUSER + 1]; /* + termination char */
|
||||
};
|
||||
|
||||
struct gsm_mncc_progress {
|
||||
int coding;
|
||||
int location;
|
||||
int descr;
|
||||
};
|
||||
|
||||
struct gsm_mncc_facility {
|
||||
int len;
|
||||
char info[GSM_MAX_FACILITY];
|
||||
};
|
||||
|
||||
struct gsm_mncc_ssversion {
|
||||
int len;
|
||||
char info[GSM_MAX_SSVERSION];
|
||||
};
|
||||
|
||||
struct gsm_mncc_cccap {
|
||||
int dtmf;
|
||||
int pcp;
|
||||
};
|
||||
|
||||
|
||||
struct gsm_mncc {
|
||||
/* context based information */
|
||||
u_int32_t msg_type;
|
||||
u_int32_t callref;
|
||||
|
||||
/* which fields are present */
|
||||
u_int32_t fields;
|
||||
|
||||
/* data derived informations (MNCC_F_ based) */
|
||||
struct gsm_mncc_bearer_cap bearer_cap;
|
||||
struct gsm_mncc_number called;
|
||||
struct gsm_mncc_number calling;
|
||||
struct gsm_mncc_number redirecting;
|
||||
struct gsm_mncc_number connected;
|
||||
struct gsm_mncc_cause cause;
|
||||
struct gsm_mncc_progress progress;
|
||||
struct gsm_mncc_useruser useruser;
|
||||
struct gsm_mncc_facility facility;
|
||||
struct gsm_mncc_cccap cccap;
|
||||
struct gsm_mncc_ssversion ssversion;
|
||||
struct {
|
||||
int sup;
|
||||
int inv;
|
||||
} clir;
|
||||
int signal;
|
||||
|
||||
/* data derived information, not MNCC_F based */
|
||||
int keypad;
|
||||
int more;
|
||||
int notify; /* 0..127 */
|
||||
int emergency;
|
||||
|
||||
unsigned char lchan_mode;
|
||||
};
|
||||
|
||||
struct gsm_trau_frame {
|
||||
u_int32_t msg_type;
|
||||
u_int32_t callref;
|
||||
unsigned char data[0];
|
||||
};
|
||||
|
||||
char *get_mncc_name(int value);
|
||||
int mncc_recv(struct gsm_network *net, int msg_type, void *arg);
|
||||
void mncc_set_cause(struct gsm_mncc *data, int loc, int val);
|
||||
|
||||
#endif
|
|
@ -5,7 +5,7 @@ sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config isdnsync
|
|||
noinst_LIBRARIES = libbsc.a libvty.a
|
||||
noinst_HEADERS = vty/cardshell.h
|
||||
|
||||
libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_04_08.c gsm_data.c \
|
||||
libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_04_08.c gsm_data.c mncc.c \
|
||||
gsm_subscriber.c msgb.c select.c chan_alloc.c timer.c debug.c db.c \
|
||||
gsm_04_11.c telnet_interface.c subchan_demux.c \
|
||||
trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c tlv_parser.c \
|
||||
|
|
|
@ -775,7 +775,7 @@ int main(int argc, char **argv)
|
|||
|
||||
handle_options(argc, argv);
|
||||
|
||||
gsmnet = gsm_network_init(1, 1, 1, GSM_BTS_TYPE_BS11);
|
||||
gsmnet = gsm_network_init(1, 1, 1, GSM_BTS_TYPE_BS11, NULL);
|
||||
if (!gsmnet) {
|
||||
fprintf(stderr, "Unable to allocate gsm network\n");
|
||||
exit(1);
|
||||
|
|
|
@ -995,7 +995,7 @@ static int bootstrap_network(void)
|
|||
}
|
||||
|
||||
/* initialize our data structures */
|
||||
gsmnet = gsm_network_init(2, BTS_TYPE, MCC, MNC);
|
||||
gsmnet = gsm_network_init(2, BTS_TYPE, MCC, MNC, mncc_recv);
|
||||
if (!gsmnet)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -1188,6 +1188,7 @@ int main(int argc, char **argv)
|
|||
signal(SIGABRT, &signal_handler);
|
||||
|
||||
while (1) {
|
||||
bsc_upqueue(gsmnet);
|
||||
bsc_select_main(0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ static const enum abis_nm_chan_comb chcomb4pchan[] = {
|
|||
/* FIXME: bounds check */
|
||||
};
|
||||
|
||||
/* Allocate a logical channel (TS) */
|
||||
/* Allocate a physical channel (TS) */
|
||||
struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts,
|
||||
enum gsm_phys_chan_config pchan)
|
||||
{
|
||||
|
@ -114,7 +114,7 @@ static const u_int8_t subslots_per_pchan[] = {
|
|||
[GSM_PCHAN_CCCH_SDCCH4] = 4,
|
||||
[GSM_PCHAN_TCH_F] = 1,
|
||||
[GSM_PCHAN_TCH_H] = 2,
|
||||
[GSM_PCHAN_SDCCH8_SACCH8C] = 8.
|
||||
[GSM_PCHAN_SDCCH8_SACCH8C] = 8,
|
||||
};
|
||||
|
||||
static struct gsm_lchan *
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -84,7 +84,8 @@ const char *gsm_chreq_name(enum gsm_chreq_reason_t c)
|
|||
}
|
||||
|
||||
struct gsm_network *gsm_network_init(unsigned int num_bts, enum gsm_bts_type bts_type,
|
||||
u_int16_t country_code, u_int16_t network_code)
|
||||
u_int16_t country_code, u_int16_t network_code,
|
||||
int (*mncc_recv)(struct gsm_network *, int, void *))
|
||||
{
|
||||
int i;
|
||||
struct gsm_network *net;
|
||||
|
@ -101,6 +102,11 @@ struct gsm_network *gsm_network_init(unsigned int num_bts, enum gsm_bts_type bts
|
|||
net->network_code = network_code;
|
||||
net->num_bts = num_bts;
|
||||
|
||||
INIT_LLIST_HEAD(&net->trans_list);
|
||||
INIT_LLIST_HEAD(&net->upqueue);
|
||||
|
||||
net->mncc_recv = mncc_recv;
|
||||
|
||||
for (i = 0; i < num_bts; i++) {
|
||||
struct gsm_bts *bts = &net->bts[i];
|
||||
int j;
|
||||
|
@ -118,7 +124,7 @@ struct gsm_network *gsm_network_init(unsigned int num_bts, enum gsm_bts_type bts
|
|||
trx->bts = bts;
|
||||
trx->nr = j;
|
||||
|
||||
for (k = 0; k < 8; k++) {
|
||||
for (k = 0; k < TRX_NR_TS; k++) {
|
||||
struct gsm_bts_trx_ts *ts = &trx->ts[k];
|
||||
int l;
|
||||
|
||||
|
|
|
@ -170,7 +170,7 @@ int main(int argc, char **argv)
|
|||
exit(2);
|
||||
}
|
||||
|
||||
gsmnet = gsm_network_init( 1, GSM_BTS_TYPE_NANOBTS_900, 1, 1);
|
||||
gsmnet = gsm_network_init(1, GSM_BTS_TYPE_NANOBTS_900, 1, 1, NULL);
|
||||
if (!gsmnet)
|
||||
exit(1);
|
||||
|
||||
|
|
|
@ -0,0 +1,382 @@
|
|||
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2009 by Andreas Eversberg <Andreas.Eversberg@versatel.de>
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <openbsc/gsm_04_08.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/mncc.h>
|
||||
|
||||
static struct mncc_names {
|
||||
char *name;
|
||||
int value;
|
||||
} mncc_names[] = {
|
||||
{"MNCC_SETUP_REQ", 0x0101},
|
||||
{"MNCC_SETUP_IND", 0x0102},
|
||||
{"MNCC_SETUP_RSP", 0x0103},
|
||||
{"MNCC_SETUP_CNF", 0x0104},
|
||||
{"MNCC_SETUP_COMPL_REQ",0x0105},
|
||||
{"MNCC_SETUP_COMPL_IND",0x0106},
|
||||
{"MNCC_CALL_CONF_IND", 0x0107},
|
||||
{"MNCC_CALL_PROC_REQ", 0x0108},
|
||||
{"MNCC_PROGRESS_REQ", 0x0109},
|
||||
{"MNCC_ALERT_REQ", 0x010a},
|
||||
{"MNCC_ALERT_IND", 0x010b},
|
||||
{"MNCC_NOTIFY_REQ", 0x010c},
|
||||
{"MNCC_NOTIFY_IND", 0x010d},
|
||||
{"MNCC_DISC_REQ", 0x010e},
|
||||
{"MNCC_DISC_IND", 0x010f},
|
||||
{"MNCC_REL_REQ", 0x0110},
|
||||
{"MNCC_REL_IND", 0x0111},
|
||||
{"MNCC_REL_CNF", 0x0112},
|
||||
{"MNCC_FACILITY_REQ", 0x0113},
|
||||
{"MNCC_FACILITY_IND", 0x0114},
|
||||
{"MNCC_START_DTMF_IND", 0x0115},
|
||||
{"MNCC_START_DTMF_RSP", 0x0116},
|
||||
{"MNCC_START_DTMF_REJ", 0x0117},
|
||||
{"MNCC_STOP_DTMF_IND", 0x0118},
|
||||
{"MNCC_STOP_DTMF_RSP", 0x0119},
|
||||
{"MNCC_MODIFY_REQ", 0x011a},
|
||||
{"MNCC_MODIFY_IND", 0x011b},
|
||||
{"MNCC_MODIFY_RSP", 0x011c},
|
||||
{"MNCC_MODIFY_CNF", 0x011d},
|
||||
{"MNCC_MODIFY_REJ", 0x011e},
|
||||
{"MNCC_HOLD_IND", 0x011f},
|
||||
{"MNCC_HOLD_CNF", 0x0120},
|
||||
{"MNCC_HOLD_REJ", 0x0121},
|
||||
{"MNCC_RETRIEVE_IND", 0x0122},
|
||||
{"MNCC_RETRIEVE_CNF", 0x0123},
|
||||
{"MNCC_RETRIEVE_REJ", 0x0124},
|
||||
{"MNCC_USERINFO_REQ", 0x0125},
|
||||
{"MNCC_USERINFO_IND", 0x0126},
|
||||
{"MNCC_REJ_REQ", 0x0127},
|
||||
{"MNCC_REJ_IND", 0x0128},
|
||||
|
||||
{"MNCC_BRIDGE", 0x0200},
|
||||
{"MNCC_FRAME_RECV", 0x0201},
|
||||
{"MNCC_FRAME_DROP", 0x0202},
|
||||
{"MNCC_LCHAN_MODIFY", 0x0203},
|
||||
|
||||
{"GSM_TRAU_FRAME", 0x0300},
|
||||
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
static LLIST_HEAD(call_list);
|
||||
|
||||
static u_int32_t new_callref = 0x00000001;
|
||||
|
||||
char *get_mncc_name(int value)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; mncc_names[i].name; i++) {
|
||||
if (mncc_names[i].value == value)
|
||||
return mncc_names[i].name;
|
||||
}
|
||||
|
||||
return "MNCC_Unknown";
|
||||
}
|
||||
|
||||
static void free_call(struct gsm_call *call)
|
||||
{
|
||||
llist_del(&call->entry);
|
||||
DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref);
|
||||
free(call);
|
||||
}
|
||||
|
||||
|
||||
struct gsm_call *get_call_ref(u_int32_t callref)
|
||||
{
|
||||
struct gsm_call *callt;
|
||||
|
||||
llist_for_each_entry(callt, &call_list, entry) {
|
||||
if (callt->callref == callref)
|
||||
return callt;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void mncc_set_cause(struct gsm_mncc *data, int loc, int val)
|
||||
{
|
||||
data->fields |= MNCC_F_CAUSE;
|
||||
data->cause.location = loc;
|
||||
data->cause.value = val;
|
||||
}
|
||||
|
||||
/* on incoming call, look up database and send setup to remote subscr. */
|
||||
static int mncc_setup_ind(struct gsm_call *call, int msg_type,
|
||||
struct gsm_mncc *setup)
|
||||
{
|
||||
struct gsm_mncc mncc;
|
||||
struct gsm_call *remote;
|
||||
|
||||
/* already have remote call */
|
||||
if (call->remote_ref)
|
||||
return 0;
|
||||
|
||||
/* create remote call */
|
||||
if (!(remote = calloc(1, sizeof(struct gsm_call)))) {
|
||||
memset(&mncc, 0, sizeof(struct gsm_mncc));
|
||||
mncc.callref = call->callref;
|
||||
mncc_set_cause(&mncc, 1, 47);
|
||||
mncc_send(call->net, MNCC_REJ_REQ, &mncc);
|
||||
free_call(call);
|
||||
return 0;
|
||||
}
|
||||
llist_add_tail(&remote->entry, &call_list);
|
||||
remote->net = call->net;
|
||||
remote->callref = new_callref++;
|
||||
DEBUGP(DMNCC, "(call %x) Creating new remote instance %x.\n",
|
||||
call->callref, remote->callref);
|
||||
|
||||
/* link remote call */
|
||||
call->remote_ref = remote->callref;
|
||||
remote->remote_ref = call->callref;
|
||||
|
||||
/* modify mode */
|
||||
memset(&mncc, 0, sizeof(struct gsm_mncc));
|
||||
mncc.callref = call->callref;
|
||||
mncc.lchan_mode = GSM48_CMODE_SPEECH_EFR;
|
||||
DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref);
|
||||
mncc_send(call->net, MNCC_LCHAN_MODIFY, &mncc);
|
||||
|
||||
/* send call proceeding */
|
||||
memset(&mncc, 0, sizeof(struct gsm_mncc));
|
||||
mncc.callref = call->callref;
|
||||
DEBUGP(DMNCC, "(call %x) Accepting call.\n", call->callref);
|
||||
mncc_send(call->net, MNCC_CALL_PROC_REQ, &mncc);
|
||||
|
||||
/* send setup to remote */
|
||||
// setup->fields |= MNCC_F_SIGNAL;
|
||||
// setup->signal = GSM48_SIGNAL_DIALTONE;
|
||||
setup->callref = remote->callref;
|
||||
DEBUGP(DMNCC, "(call %x) Forwarding SETUP to remote.\n", call->callref);
|
||||
return mncc_send(remote->net, MNCC_SETUP_REQ, setup);
|
||||
}
|
||||
|
||||
static int mncc_alert_ind(struct gsm_call *call, int msg_type,
|
||||
struct gsm_mncc *alert)
|
||||
{
|
||||
struct gsm_call *remote;
|
||||
|
||||
/* send alerting to remote */
|
||||
if (!(remote = get_call_ref(call->remote_ref)))
|
||||
return 0;
|
||||
alert->callref = remote->callref;
|
||||
DEBUGP(DMNCC, "(call %x) Forwarding ALERT to remote.\n", call->callref);
|
||||
return mncc_send(remote->net, MNCC_ALERT_REQ, alert);
|
||||
}
|
||||
|
||||
static int mncc_notify_ind(struct gsm_call *call, int msg_type,
|
||||
struct gsm_mncc *notify)
|
||||
{
|
||||
struct gsm_call *remote;
|
||||
|
||||
/* send notify to remote */
|
||||
if (!(remote = get_call_ref(call->remote_ref)))
|
||||
return 0;
|
||||
notify->callref = remote->callref;
|
||||
DEBUGP(DMNCC, "(call %x) Forwarding NOTIF to remote.\n", call->callref);
|
||||
return mncc_send(remote->net, MNCC_NOTIFY_REQ, notify);
|
||||
}
|
||||
|
||||
static int mncc_setup_cnf(struct gsm_call *call, int msg_type,
|
||||
struct gsm_mncc *connect)
|
||||
{
|
||||
struct gsm_mncc connect_ack;
|
||||
struct gsm_call *remote;
|
||||
u_int32_t refs[2];
|
||||
|
||||
/* acknowledge connect */
|
||||
memset(&connect_ack, 0, sizeof(struct gsm_mncc));
|
||||
connect_ack.callref = call->callref;
|
||||
DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref);
|
||||
mncc_send(call->net, MNCC_SETUP_COMPL_REQ, &connect_ack);
|
||||
|
||||
/* send connect message to remote */
|
||||
if (!(remote = get_call_ref(call->remote_ref)))
|
||||
return 0;
|
||||
connect->callref = remote->callref;
|
||||
DEBUGP(DMNCC, "(call %x) Sending CONNECT to remote.\n", call->callref);
|
||||
mncc_send(remote->net, MNCC_SETUP_RSP, connect);
|
||||
|
||||
/* bridge tch */
|
||||
refs[0] = call->callref;
|
||||
refs[1] = call->remote_ref;
|
||||
DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref);
|
||||
return mncc_send(call->net, MNCC_BRIDGE, refs);
|
||||
}
|
||||
|
||||
static int mncc_disc_ind(struct gsm_call *call, int msg_type,
|
||||
struct gsm_mncc *disc)
|
||||
{
|
||||
struct gsm_call *remote;
|
||||
|
||||
/* send release */
|
||||
DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n",
|
||||
call->callref, disc->cause.value);
|
||||
mncc_send(call->net, MNCC_REL_REQ, disc);
|
||||
|
||||
/* send disc to remote */
|
||||
if (!(remote = get_call_ref(call->remote_ref))) {
|
||||
return 0;
|
||||
}
|
||||
disc->callref = remote->callref;
|
||||
DEBUGP(DMNCC, "(call %x) Disconnecting remote with cause %d\n",
|
||||
remote->callref, disc->cause.value);
|
||||
return mncc_send(remote->net, MNCC_DISC_REQ, disc);
|
||||
}
|
||||
|
||||
static int mncc_rel_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *rel)
|
||||
{
|
||||
struct gsm_call *remote;
|
||||
|
||||
/* send release to remote */
|
||||
if (!(remote = get_call_ref(call->remote_ref))) {
|
||||
free_call(call);
|
||||
return 0;
|
||||
}
|
||||
rel->callref = remote->callref;
|
||||
DEBUGP(DMNCC, "(call %x) Releasing remote with cause %d\n",
|
||||
call->callref, rel->cause.value);
|
||||
mncc_send(remote->net, MNCC_REL_REQ, rel);
|
||||
|
||||
free_call(call);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *rel)
|
||||
{
|
||||
free_call(call);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mncc_recv(struct gsm_network *net, int msg_type, void *arg)
|
||||
{
|
||||
struct gsm_mncc *data = arg;
|
||||
int callref;
|
||||
struct gsm_call *call = NULL, *callt;
|
||||
int rc = 0;
|
||||
|
||||
/* Special messages */
|
||||
switch(msg_type) {
|
||||
}
|
||||
|
||||
/* find callref */
|
||||
callref = data->callref;
|
||||
llist_for_each_entry(callt, &call_list, entry) {
|
||||
if (callt->callref == callref) {
|
||||
call = callt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* create callref, if setup is received */
|
||||
if (!call) {
|
||||
if (msg_type != MNCC_SETUP_IND)
|
||||
return 0; /* drop */
|
||||
/* create call */
|
||||
if (!(call = calloc(1, sizeof(struct gsm_call)))) {
|
||||
struct gsm_mncc rel;
|
||||
|
||||
memset(&rel, 0, sizeof(struct gsm_mncc));
|
||||
rel.callref = callref;
|
||||
mncc_set_cause(&rel, 1, 47);
|
||||
mncc_send(net, MNCC_REL_REQ, &rel);
|
||||
return 0;
|
||||
}
|
||||
llist_add_tail(&call->entry, &call_list);
|
||||
call->net = net;
|
||||
call->callref = callref;
|
||||
DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref);
|
||||
}
|
||||
|
||||
DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref,
|
||||
get_mncc_name(msg_type));
|
||||
|
||||
switch(msg_type) {
|
||||
case MNCC_SETUP_IND:
|
||||
rc = mncc_setup_ind(call, msg_type, arg);
|
||||
break;
|
||||
case MNCC_SETUP_CNF:
|
||||
rc = mncc_setup_cnf(call, msg_type, arg);
|
||||
break;
|
||||
case MNCC_SETUP_COMPL_IND:
|
||||
break;
|
||||
case MNCC_CALL_CONF_IND:
|
||||
/* we now need to MODIFY the channel */
|
||||
data->lchan_mode = GSM48_CMODE_SPEECH_EFR;
|
||||
mncc_send(call->net, MNCC_LCHAN_MODIFY, data);
|
||||
break;
|
||||
case MNCC_ALERT_IND:
|
||||
rc = mncc_alert_ind(call, msg_type, arg);
|
||||
break;
|
||||
case MNCC_NOTIFY_IND:
|
||||
rc = mncc_notify_ind(call, msg_type, arg);
|
||||
break;
|
||||
case MNCC_DISC_IND:
|
||||
rc = mncc_disc_ind(call, msg_type, arg);
|
||||
break;
|
||||
case MNCC_REL_IND:
|
||||
case MNCC_REJ_IND:
|
||||
rc = mncc_rel_ind(call, msg_type, arg);
|
||||
break;
|
||||
case MNCC_REL_CNF:
|
||||
rc = mncc_rel_cnf(call, msg_type, arg);
|
||||
break;
|
||||
case MNCC_FACILITY_IND:
|
||||
break;
|
||||
case MNCC_START_DTMF_IND:
|
||||
break;
|
||||
case MNCC_STOP_DTMF_IND:
|
||||
break;
|
||||
case MNCC_MODIFY_IND:
|
||||
mncc_set_cause(data, 1, 79);
|
||||
DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n",
|
||||
call->callref, data->cause.value);
|
||||
rc = mncc_send(net, MNCC_MODIFY_REJ, data);
|
||||
break;
|
||||
case MNCC_MODIFY_CNF:
|
||||
break;
|
||||
case MNCC_HOLD_IND:
|
||||
mncc_set_cause(data, 1, 79);
|
||||
DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n",
|
||||
call->callref, data->cause.value);
|
||||
rc = mncc_send(net, MNCC_HOLD_REJ, data);
|
||||
break;
|
||||
case MNCC_RETRIEVE_IND:
|
||||
mncc_set_cause(data, 1, 79);
|
||||
DEBUGP(DMNCC, "(call %x) Rejecting RETRIEVE with cause %d\n",
|
||||
call->callref, data->cause.value);
|
||||
rc = mncc_send(net, MNCC_RETRIEVE_REJ, data);
|
||||
break;
|
||||
default:
|
||||
DEBUGP(DMNCC, "(call %x) Message unhandled\n", callref);
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
|
@ -221,6 +221,7 @@ static void _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
|
|||
return;
|
||||
}
|
||||
|
||||
DEBUGP(DPAG, "Start paging on bts %d.\n", bts->nr);
|
||||
req = (struct gsm_paging_request *)malloc(sizeof(*req));
|
||||
memset(req, 0, sizeof(*req));
|
||||
req->subscr = subscr_get(subscr);
|
||||
|
@ -263,9 +264,12 @@ static void _paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *sub
|
|||
llist_for_each_entry_safe(req, req2, &bts_entry->pending_requests,
|
||||
entry) {
|
||||
if (req->subscr == subscr) {
|
||||
if (lchan && req->cbfn)
|
||||
if (lchan && req->cbfn) {
|
||||
DEBUGP(DPAG, "Stop paging on bts %d, calling cbfn.\n", bts->nr);
|
||||
req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED,
|
||||
NULL, lchan, req->cbfn_param);
|
||||
} else
|
||||
DEBUGP(DPAG, "Stop paging on bts %d silently.\n", bts->nr);
|
||||
paging_remove_request(&bts->paging, req);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -145,6 +145,9 @@ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
|
|||
u_int8_t trau_bits_out[TRAU_FRAME_BITS];
|
||||
struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss);
|
||||
struct subch_mux *mx;
|
||||
struct upqueue_entry *ue;
|
||||
struct msgb *msg;
|
||||
struct gsm_trau_frame *frame;
|
||||
int rc;
|
||||
|
||||
/* decode TRAU, change it to downlink, re-encode */
|
||||
|
@ -152,8 +155,23 @@ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!dst_e1_ss)
|
||||
return -EINVAL;
|
||||
if (!dst_e1_ss) {
|
||||
/* frame shall be sent to upqueue */
|
||||
if (!(ue = lookup_trau_upqueue(src_e1_ss)))
|
||||
return -EINVAL;
|
||||
if (!ue->callref)
|
||||
return -EINVAL;
|
||||
msg = msgb_alloc(sizeof(struct gsm_trau_frame) + sizeof(tf));
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
frame = (struct gsm_trau_frame *)msg->data;
|
||||
frame->msg_type = GSM_TRAU_FRAME;
|
||||
frame->callref = ue->callref;
|
||||
memcpy(frame->data, &tf, sizeof(tf));
|
||||
msgb_enqueue(&ue->net->upqueue, msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
|
||||
if (!mx)
|
||||
|
|
|
@ -326,6 +326,8 @@ static void lchan_dump_vty(struct vty *vty, struct gsm_lchan *lchan)
|
|||
vty_out(vty, " No Subscriber%s", VTY_NEWLINE);
|
||||
}
|
||||
|
||||
#if 0
|
||||
TODO: callref and remote callref of call must be resolved to get gsm_trans object
|
||||
static void call_dump_vty(struct vty *vty, struct gsm_call *call)
|
||||
{
|
||||
vty_out(vty, "Call Type %u, State %u, Transaction ID %u%s",
|
||||
|
@ -349,6 +351,7 @@ static void call_dump_vty(struct vty *vty, struct gsm_call *call)
|
|||
} else
|
||||
vty_out(vty, "Call has no Called Subscriber%s", VTY_NEWLINE);
|
||||
}
|
||||
#endif
|
||||
|
||||
DEFUN(show_lchan,
|
||||
show_lchan_cmd,
|
||||
|
|
Loading…
Reference in New Issue