summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2009-06-14 22:15:37 +0800
committerHarald Welte <laforge@gnumonks.org>2009-06-14 22:15:37 +0800
commita717d5de8e00ae960b31eb3d0f596a51941ad73c (patch)
tree8e6162c4a84b2604e42ab74e55cbd58500973594
parentff87157b65b00dae1436e4805b84a6ee3a4144e4 (diff)
parent7563ac97c428619f7d69b9f33ca1832127ea082d (diff)
Merge branch 'mncc-harald'
-rw-r--r--openbsc/include/openbsc/gsm_04_08.h107
-rw-r--r--openbsc/include/openbsc/gsm_data.h118
-rw-r--r--openbsc/include/openbsc/gsm_subscriber.h1
-rw-r--r--openbsc/include/openbsc/mncc.h210
-rw-r--r--openbsc/src/Makefile.am2
-rw-r--r--openbsc/src/abis_nm.c2
-rw-r--r--openbsc/src/bs11_config.c2
-rw-r--r--openbsc/src/bsc_hack.c3
-rw-r--r--openbsc/src/chan_alloc.c4
-rw-r--r--openbsc/src/gsm_04_08.c2381
-rw-r--r--openbsc/src/gsm_04_11.c3
-rw-r--r--openbsc/src/gsm_data.c10
-rw-r--r--openbsc/src/ipaccess-config.c2
-rw-r--r--openbsc/src/mncc.c387
-rw-r--r--openbsc/src/paging.c6
-rw-r--r--openbsc/src/trau_mux.c22
-rw-r--r--openbsc/src/vty_interface.c3
17 files changed, 2817 insertions, 446 deletions
diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h
index 03922acc7..2fe23d1fc 100644
--- a/openbsc/include/openbsc/gsm_04_08.h
+++ b/openbsc/include/openbsc/gsm_04_08.h
@@ -482,6 +482,59 @@ enum gsm48_rr_cause {
GSM48_RR_CAUSE_PROT_ERROR_UNSPC = 0x6f,
};
+/* Section 10.5.4.11 CC Cause / Table 10.5.123 */
+enum gsm48_cc_cause {
+ GSM48_CC_CAUSE_UNASSIGNED_NR = 1,
+ GSM48_CC_CAUSE_NO_ROUTE = 3,
+ GSM48_CC_CAUSE_CHAN_UNACCEPT = 6,
+ GSM48_CC_CAUSE_OP_DET_BARRING = 8,
+ GSM48_CC_CAUSE_NORM_CALL_CLEAR = 16,
+ GSM48_CC_CAUSE_USER_BUSY = 17,
+ GSM48_CC_CAUSE_USER_NOTRESPOND = 18,
+ GSM48_CC_CAUSE_USER_ALERTING_NA = 19,
+ GSM48_CC_CAUSE_CALL_REJECTED = 21,
+ GSM48_CC_CAUSE_NUMBER_CHANGED = 22,
+ GSM48_CC_CAUSE_PRE_EMPTION = 25,
+ GSM48_CC_CAUSE_NONSE_USER_CLR = 26,
+ GSM48_CC_CAUSE_DEST_OOO = 27,
+ GSM48_CC_CAUSE_INV_NR_FORMAT = 28,
+ GSM48_CC_CAUSE_FACILITY_REJ = 29,
+ GSM48_CC_CAUSE_RESP_STATUS_INQ = 30,
+ GSM48_CC_CAUSE_NORMAL_UNSPEC = 31,
+ GSM48_CC_CAUSE_NO_CIRCUIT_CHAN = 34,
+ GSM48_CC_CAUSE_NETWORK_OOO = 38,
+ GSM48_CC_CAUSE_TEMP_FAILURE = 41,
+ GSM48_CC_CAUSE_SWITCH_CONG = 42,
+ GSM48_CC_CAUSE_ACC_INF_DISCARD = 43,
+ GSM48_CC_CAUSE_REQ_CHAN_UNAVAIL = 44,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL = 47,
+ GSM48_CC_CAUSE_QOS_UNAVAIL = 49,
+ GSM48_CC_CAUSE_REQ_FAC_NOT_SUBSC= 50,
+ GSM48_CC_CAUSE_INC_BARRED_CUG = 55,
+ GSM48_CC_CAUSE_BEARER_CAP_UNAUTH= 57,
+ GSM48_CC_CAUSE_BEARER_CA_UNAVAIL= 58,
+ GSM48_CC_CAUSE_SERV_OPT_UNAVAIL = 63,
+ GSM48_CC_CAUSE_BEARERSERV_UNIMPL= 65,
+ GSM48_CC_CAUSE_ACM_GE_ACM_MAX = 68,
+ GSM48_CC_CAUSE_REQ_FAC_NOTIMPL = 69,
+ GSM48_CC_CAUSE_RESTR_BCAP_AVAIL = 70,
+ GSM48_CC_CAUSE_SERV_OPT_UNIMPL = 79,
+ GSM48_CC_CAUSE_INVAL_TRANS_ID = 81,
+ GSM48_CC_CAUSE_USER_NOT_IN_CUG = 87,
+ GSM48_CC_CAUSE_INCOMPAT_DEST = 88,
+ GSM48_CC_CAUSE_INVAL_TRANS_NET = 91,
+ GSM48_CC_CAUSE_SEMANTIC_INCORR = 95,
+ GSM48_CC_CAUSE_INVAL_MAND_INF = 96,
+ GSM48_CC_CAUSE_MSGTYPE_NOTEXIST = 97,
+ GSM48_CC_CAUSE_MSGTYPE_INCOMPAT = 98,
+ GSM48_CC_CAUSE_IE_NOTEXIST = 99,
+ GSM48_CC_CAUSE_COND_IE_ERR = 100,
+ GSM48_CC_CAUSE_MSG_INCOMP_STATE = 101,
+ GSM48_CC_CAUSE_RECOVERY_TIMER = 102,
+ GSM48_CC_CAUSE_PROTO_ERR = 111,
+ GSM48_CC_CAUSE_INTERWORKING = 127,
+};
+
/* Annex G, GSM specific cause values for mobility management */
enum gsm48_reject_value {
GSM48_REJECT_IMSI_UNKNOWN_IN_HLR = 2,
@@ -540,10 +593,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 +654,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 +664,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);
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index ad6401e9c..a401a4780 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -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]))
@@ -38,7 +67,7 @@ typedef int gsm_cbfn(unsigned int hooknum,
* Use the channel. As side effect the lchannel recycle timer
* will be started.
*/
-#define LCHAN_RELEASE_TIMEOUT 4, 0
+#define LCHAN_RELEASE_TIMEOUT 10, 0
#define use_lchan(lchan) \
do { lchan->use_count++; \
DEBUGP(DCC, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) increases usage to: %d\n", \
@@ -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 */
-
- /* the 'local' channel */
- struct gsm_lchan *local_lchan;
- /* the 'remote' channel */
- struct gsm_lchan *remote_lchan;
+/* One transaction */
+struct gsm_trans {
+ /* Entry in list of all transactions */
+ struct llist_head entry;
- /* the 'remote' subscriber */
- struct gsm_subscriber *called_subscr;
-};
+ /* Network */
+ struct gsm_network *network;
+ /* The current transaction ID */
+ u_int8_t transaction_id;
+
+ /* The LCHAN that we're part of */
+ struct gsm_lchan *lchan;
-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,
-};
+ /* To whom we are allocated at the moment */
+ struct gsm_subscriber *subscr;
-enum gsm_chan_t {
- GSM_LCHAN_NONE,
- GSM_LCHAN_SDCCH,
- GSM_LCHAN_TCH_F,
- GSM_LCHAN_TCH_H,
- GSM_LCHAN_UNKNOWN,
-};
+ /* reference */
+ u_int32_t callref;
+ /* current call state */
+ int state;
-/* 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,
+ /* 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 */
};
+
/* 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);
diff --git a/openbsc/include/openbsc/gsm_subscriber.h b/openbsc/include/openbsc/gsm_subscriber.h
index 780d8ede0..dc4f6d7a6 100644
--- a/openbsc/include/openbsc/gsm_subscriber.h
+++ b/openbsc/include/openbsc/gsm_subscriber.h
@@ -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];
diff --git a/openbsc/include/openbsc/mncc.h b/openbsc/include/openbsc/mncc.h
new file mode 100644
index 000000000..c04a81f6d
--- /dev/null
+++ b/openbsc/include/openbsc/mncc.h
@@ -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
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index fbbbfdc94..f4b8e552d 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -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 \
diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c
index 83e6bbb48..905289b6d 100644
--- a/openbsc/src/abis_nm.c
+++ b/openbsc/src/abis_nm.c
@@ -694,7 +694,7 @@ static int abis_nm_rx_statechg_rep(struct msgb *mb)
}
if (TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) {
new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE);
- DEBUGPC(DNM, "ADM=%02x ", nm_adm_name(new_state.administrative));
+ DEBUGPC(DNM, "ADM=%02s ", nm_adm_name(new_state.administrative));
}
DEBUGPC(DNM, "\n");
diff --git a/openbsc/src/bs11_config.c b/openbsc/src/bs11_config.c
index 656feb453..386b865ca 100644
--- a/openbsc/src/bs11_config.c
+++ b/openbsc/src/bs11_config.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);
diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c
index 388840793..e41b0e382 100644
--- a/openbsc/src/bsc_hack.c
+++ b/openbsc/src/bsc_hack.c
@@ -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);
}
}
diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c
index 77a4f57bc..96632bcb9 100644
--- a/openbsc/src/chan_alloc.c
+++ b/openbsc/src/chan_alloc.c
@@ -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 *
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index 90b88dfb3..8e5bfe88f 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -113,6 +113,108 @@ static const char *rr_cause_names[] = {
[GSM48_RR_CAUSE_PROT_ERROR_UNSPC] = "Protocol error unspecified",
};
+static const char *cc_state_names[] = {
+ "NULL",
+ "INITIATED",
+ "illegal state 2",
+ "MO_CALL_PROC",
+ "CALL_DELIVERED",
+ "illegal state 5",
+ "CALL_PRESENT",
+ "CALL_RECEIVED",
+ "CONNECT_REQUEST",
+ "MO_TERM_CALL_CONF",
+ "ACTIVE",
+ "DISCONNECT_REQ",
+ "DISCONNECT_IND",
+ "illegal state 13",
+ "illegal state 14",
+ "illegal state 15",
+ "illegal state 16",
+ "illegal state 17",
+ "illegal state 18",
+ "RELEASE_REQ",
+ "illegal state 20",
+ "illegal state 21",
+ "illegal state 22",
+ "illegal state 23",
+ "illegal state 24",
+ "illegal state 25",
+ "MO_ORIG_MODIFY",
+ "MO_TERM_MODIFY",
+ "CONNECT_IND",
+ "illegal state 29",
+ "illegal state 30",
+ "illegal state 31",
+};
+
+static const char *cc_msg_names[] = {
+ "unknown 0x00",
+ "ALERTING",
+ "CALL_PROC",
+ "PROGRESS",
+ "ESTAB",
+ "SETUP",
+ "ESTAB_CONF",
+ "CONNECT",
+ "CALL_CONF",
+ "START_CC",
+ "unknown 0x0a",
+ "RECALL",
+ "unknown 0x0c",
+ "unknown 0x0d",
+ "EMERG_SETUP",
+ "CONNECT_ACK",
+ "USER_INFO",
+ "unknown 0x11",
+ "unknown 0x12",
+ "MODIFY_REJECT",
+ "unknown 0x14",
+ "unknown 0x15",
+ "unknown 0x16",
+ "MODIFY",
+ "HOLD",
+ "HOLD_ACK",
+ "HOLD_REJ",
+ "unknown 0x1b",
+ "RETR",
+ "RETR_ACK",
+ "RETR_REJ",
+ "MODIFY_COMPL",
+ "unknown 0x20",
+ "unknown 0x21",
+ "unknown 0x22",
+ "unknown 0x23",
+ "unknown 0x24",
+ "DISCONNECT",
+ "unknown 0x26",
+ "unknown 0x27",
+ "unknown 0x28",
+ "unknown 0x29",
+ "RELEASE_COMPL",
+ "unknown 0x2b",
+ "unknown 0x2c",
+ "RELEASE",
+ "unknown 0x2e",
+ "unknown 0x2f",
+ "unknown 0x30",
+ "STOP_DTMF",
+ "STOP_DTMF_ACK",
+ "unknown 0x33",
+ "STATUS_ENQ",
+ "START_DTMF",
+ "START_DTMF_ACK",
+ "START_DTMF_REJ",
+ "unknown 0x38",
+ "CONG_CTRL",
+ "FACILITY",
+ "unknown 0x3b",
+ "STATUS",
+ "unknown 0x3c",
+ "NOTIFY",
+ "unknown 0x3f",
+};
+
static char strbuf[64];
static const char *rr_cause_name(u_int8_t cause)
@@ -186,6 +288,7 @@ int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi);
static int gsm48_tx_simple(struct gsm_lchan *lchan,
u_int8_t pdisc, u_int8_t msg_type);
static void schedule_reject(struct gsm_lchan *lchan);
+void free_trans(struct gsm_trans *trans);
struct gsm_lai {
u_int16_t mcc;
@@ -206,6 +309,8 @@ void gsm0408_set_reject_cause(int cause)
reject_cause = cause;
}
+static u_int32_t new_callref = 0x80000001;
+
static int authorize_subscriber(struct gsm_loc_updating_operation *loc,
struct gsm_subscriber *subscriber)
{
@@ -265,6 +370,8 @@ static int gsm0408_authorize(struct gsm_lchan *lchan, struct msgb *msg)
static int gsm0408_handle_lchan_signal(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
+ struct gsm_trans *trans, *temp;
+
if (subsys != SS_LCHAN || signal != S_LCHAN_UNEXPECTED_RELEASE)
return 0;
@@ -275,6 +382,12 @@ static int gsm0408_handle_lchan_signal(unsigned int subsys, unsigned int signal,
struct gsm_lchan *lchan = (struct gsm_lchan *)handler_data;
release_loc_updating_req(lchan);
+ /* Free all transactions that are associated with the released lchan */
+ llist_for_each_entry_safe(trans, temp, &lchan->ts->trx->bts->network->trans_list, entry) {
+ if (trans->lchan == lchan)
+ free_trans(trans);
+ }
+
return 0;
}
@@ -411,8 +524,7 @@ int encode_bcd_number(u_int8_t *bcd_lv, u_int8_t max_len,
}
/* decode 'bearer capability' */
-static int decode_bearer_cap(int *transfer, int *mode, int *coding,
- int *radio, int *speech_ctm, int *speech_ver,
+static int decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
const u_int8_t *lv)
{
u_int8_t in_len = lv[0];
@@ -421,13 +533,13 @@ static int decode_bearer_cap(int *transfer, int *mode, int *coding,
if (in_len < 1)
return -EINVAL;
- speech_ver[0] = -1; /* end of list, of maximum 7 values */
+ bcap->speech_ver[0] = -1; /* end of list, of maximum 7 values */
/* octet 3 */
- *transfer = lv[1] & 0x07;
- *mode = (lv[1] & 0x08) >> 3;
- *coding = (lv[1] & 0x10) >> 4;
- *radio = (lv[1] & 0x60) >> 5;
+ bcap->transfer = lv[1] & 0x07;
+ bcap->mode = (lv[1] & 0x08) >> 3;
+ bcap->coding = (lv[1] & 0x10) >> 4;
+ bcap->radio = (lv[1] & 0x60) >> 5;
i = 1;
s = 0;
@@ -435,10 +547,10 @@ static int decode_bearer_cap(int *transfer, int *mode, int *coding,
i++; /* octet 3a etc */
if (in_len < i)
return 0;
- speech_ver[s++] = lv[i] & 0x0f;
- speech_ver[s] = -1; /* end of list */
+ bcap->speech_ver[s++] = lv[i] & 0x0f;
+ bcap->speech_ver[s] = -1; /* end of list */
if (i == 2) /* octet 3a */
- *speech_ctm = (lv[i] & 0x20) >> 5;
+ bcap->speech_ctm = (lv[i] & 0x20) >> 5;
if (s == 7) /* maximum speech versions + end of list */
return 0;
}
@@ -447,24 +559,23 @@ static int decode_bearer_cap(int *transfer, int *mode, int *coding,
}
/* encode 'bearer capability' */
-static int encode_bearer_cap(int lv_only, struct msgb *msg, int transfer,
- int mode, int coding, int radio, int speech_ctm,
- int *speech_ver)
+static int encode_bearer_cap(struct msgb *msg, int lv_only,
+ const struct gsm_mncc_bearer_cap *bcap)
{
u_int8_t lv[32 + 1];
int i, s;
- lv[1] = transfer;
- lv[1] |= mode << 3;
- lv[1] |= coding << 4;
- lv[1] |= radio << 5;
+ lv[1] = bcap->transfer;
+ lv[1] |= bcap->mode << 3;
+ lv[1] |= bcap->coding << 4;
+ lv[1] |= bcap->radio << 5;
i = 1;
- for (s = 0; speech_ver[s] >= 0; s++) {
+ for (s = 0; bcap->speech_ver[s] >= 0; s++) {
i++; /* octet 3a etc */
- lv[i] = speech_ver[s];
+ lv[i] = bcap->speech_ver[s];
if (i == 2) /* octet 3a */
- lv[i] |= speech_ctm << 5;
+ lv[i] |= bcap->speech_ctm << 5;
}
lv[i] |= 0x80; /* last IE of octet 3 etc */
@@ -478,7 +589,7 @@ static int encode_bearer_cap(int lv_only, struct msgb *msg, int transfer,
}
/* decode 'call control cap' */
-static int decode_cccap(int *dtmf, int *pcp, const u_int8_t *lv)
+static int decode_cccap(struct gsm_mncc_cccap *ccap, const u_int8_t *lv)
{
u_int8_t in_len = lv[0];
@@ -486,15 +597,15 @@ static int decode_cccap(int *dtmf, int *pcp, const u_int8_t *lv)
return -EINVAL;
/* octet 3 */
- *dtmf = lv[1] & 0x01;
- *pcp = (lv[1] & 0x02) >> 1;
+ ccap->dtmf = lv[1] & 0x01;
+ ccap->pcp = (lv[1] & 0x02) >> 1;
return 0;
}
/* decode 'called party BCD number' */
-static int decode_called(int *type, int *plan, char *number,
- int number_len, const u_int8_t *lv)
+static int decode_called(struct gsm_mncc_number *called,
+ const u_int8_t *lv)
{
u_int8_t in_len = lv[0];
@@ -502,27 +613,28 @@ static int decode_called(int *type, int *plan, char *number,
return -EINVAL;
/* octet 3 */
- *plan = lv[1] & 0x0f;
- *type = (lv[1] & 0x70) >> 4;
+ called->plan = lv[1] & 0x0f;
+ called->type = (lv[1] & 0x70) >> 4;
/* octet 4..N */
- decode_bcd_number(number, number_len, lv, 1);
+ decode_bcd_number(called->number, sizeof(called->number), lv, 1);
return 0;
}
/* encode 'called party BCD number' */
-static int encode_called(struct msgb *msg, int type, int plan, char *number)
+static int encode_called(struct msgb *msg,
+ const struct gsm_mncc_number *called)
{
u_int8_t lv[18];
int ret;
/* octet 3 */
- lv[1] = plan;
- lv[1] |= type << 4;
+ lv[1] = called->plan;
+ lv[1] |= called->type << 4;
/* octet 4..N, octet 2 */
- ret = encode_bcd_number(lv, sizeof(lv), 1, number);
+ ret = encode_bcd_number(lv, sizeof(lv), 1, called->number);
if (ret < 0)
return ret;
@@ -532,28 +644,28 @@ static int encode_called(struct msgb *msg, int type, int plan, char *number)
}
/* encode callerid of various IEs */
-static int encode_callerid(struct msgb *msg, int ie, int type, int plan,
- int present, int screen, char *number)
+static int encode_callerid(struct msgb *msg, int ie,
+ const struct gsm_mncc_number *callerid)
{
u_int8_t lv[13];
int h_len = 1;
int ret;
/* octet 3 */
- lv[1] = plan;
- lv[1] |= type << 4;
+ lv[1] = callerid->plan;
+ lv[1] |= callerid->type << 4;
- if (present || screen) {
+ if (callerid->present || callerid->screen) {
/* octet 3a */
- lv[2] = screen;
- lv[2] |= present << 5;
+ lv[2] = callerid->screen;
+ lv[2] |= callerid->present << 5;
lv[2] |= 0x80;
h_len++;
} else
lv[1] |= 0x80;
/* octet 4..N, octet 2 */
- ret = encode_bcd_number(lv, sizeof(lv), h_len, number);
+ ret = encode_bcd_number(lv, sizeof(lv), h_len, callerid->number);
if (ret < 0)
return ret;
@@ -563,8 +675,7 @@ static int encode_callerid(struct msgb *msg, int ie, int type, int plan,
}
/* decode 'cause' */
-static int decode_cause(int *location, int *coding, int *rec, int *rec_val,
- int *value, u_char *diag, u_int *diag_len,
+static int decode_cause(struct gsm_mncc_cause *cause,
const u_int8_t *lv)
{
u_int8_t in_len = lv[0];
@@ -573,25 +684,25 @@ static int decode_cause(int *location, int *coding, int *rec, int *rec_val,
if (in_len < 2)
return -EINVAL;
- *diag_len = 0;
+ cause->diag_len = 0;
/* octet 3 */
- *location = lv[1] & 0x0f;
- *coding = (lv[1] & 0x60) >> 5;
+ cause->location = lv[1] & 0x0f;
+ cause->coding = (lv[1] & 0x60) >> 5;
i = 1;
if (!(lv[i] & 0x80)) {
i++; /* octet 3a */
if (in_len < i+1)
return 0;
- *rec = 1;
- *rec_val = lv[i] & 0x7f;
+ cause->rec = 1;
+ cause->rec_val = lv[i] & 0x7f;
}
i++;
/* octet 4 */
- *value = lv[i] & 0x7f;
+ cause->value = lv[i] & 0x7f;
i++;
if (in_len < i) /* no diag */
@@ -601,42 +712,41 @@ static int decode_cause(int *location, int *coding, int *rec, int *rec_val,
return 0;
/* octet 5-N */
- memcpy(diag, lv + i, in_len - (i-1));
- *diag_len = in_len - (i-1);
+ memcpy(cause->diag, lv + i, in_len - (i-1));
+ cause->diag_len = in_len - (i-1);
return 0;
}
/* encode 'cause' */
-static int encode_cause(int lv_only, struct msgb *msg, int location,
- int coding, int rec, int rec_val, int value,
- u_char *diag, u_int diag_len)
+static int encode_cause(struct msgb *msg, int lv_only,
+ const struct gsm_mncc_cause *cause)
{
u_int8_t lv[32+4];
int i;
- if (diag_len > 32)
+ if (cause->diag_len > 32)
return -EINVAL;
/* octet 3 */
- lv[1] = location;
- lv[1] |= coding << 5;
+ lv[1] = cause->location;
+ lv[1] |= cause->coding << 5;
i = 1;
- if (rec) {
+ if (cause->rec) {
i++; /* octet 3a */
- lv[i] = rec_val;
+ lv[i] = cause->rec_val;
}
lv[i] |= 0x80; /* end of octet 3 */
/* octet 4 */
i++;
- lv[i] = 0x80 | value;
+ lv[i] = 0x80 | cause->value;
/* octet 5-N */
- if (diag_len) {
- memcpy(lv + i, diag, diag_len);
- i += diag_len;
+ if (cause->diag_len) {
+ memcpy(lv + i, cause->diag, cause->diag_len);
+ i += cause->diag_len;
}
lv[0] = i;
@@ -649,31 +759,28 @@ static int encode_cause(int lv_only, struct msgb *msg, int location,
}
/* encode 'calling number' */
-static int encode_calling(struct msgb *msg, int type, int plan, int present,
- int screen, char *number)
+static int encode_calling(struct msgb *msg,
+ const struct gsm_mncc_number *calling)
{
- return encode_callerid(msg, GSM48_IE_CALLING_BCD, type, plan,
- present, screen, number);
+ return encode_callerid(msg, GSM48_IE_CALLING_BCD, calling);
}
/* encode 'connected number' */
-static int encode_connected(struct msgb *msg, int type, int plan, int present,
- int screen, char *number)
+static int encode_connected(struct msgb *msg,
+ const struct gsm_mncc_number *connected)
{
- return encode_callerid(msg, GSM48_IE_CONN_BCD, type, plan,
- present, screen, number);
+ return encode_callerid(msg, GSM48_IE_CONN_BCD, connected);
}
/* encode 'redirecting number' */
-static int encode_redirecting(struct msgb *msg, int type, int plan, int present,
- int screen, char *number)
+static int encode_redirecting(struct msgb *msg,
+ const struct gsm_mncc_number *redirecting)
{
- return encode_callerid(msg, GSM48_IE_REDIR_BCD, type, plan,
- present, screen, number);
+ return encode_callerid(msg, GSM48_IE_REDIR_BCD, redirecting);
}
/* decode 'facility' */
-static int decode_facility(char *info, int info_len, int *len,
+static int decode_facility(struct gsm_mncc_facility *facility,
const u_int8_t *lv)
{
u_int8_t in_len = lv[0];
@@ -681,25 +788,26 @@ static int decode_facility(char *info, int info_len, int *len,
if (in_len < 1)
return -EINVAL;
- if (in_len > info_len)
+ if (in_len > sizeof(facility->info))
return -EINVAL;
- memcpy(info, lv+1, in_len);
- *len = in_len;
+ memcpy(facility->info, lv+1, in_len);
+ facility->len = in_len;
return 0;
}
/* encode 'facility' */
-static int encode_facility(int lv_only, struct msgb *msg, char *info, int len)
+static int encode_facility(struct msgb *msg, int lv_only,
+ const struct gsm_mncc_facility *facility)
{
u_int8_t lv[GSM_MAX_FACILITY + 1];
- if (len < 1 || len > GSM_MAX_FACILITY)
+ if (facility->len < 1 || facility->len > GSM_MAX_FACILITY)
return -EINVAL;
- memcpy(lv+1, info, len);
- lv[0] = len;
+ memcpy(lv+1, facility->info, facility->len);
+ lv[0] = facility->len;
if (lv_only)
msgb_lv_put(msg, lv[0], lv+1);
else
@@ -754,7 +862,7 @@ static int encode_keypad(struct msgb *msg, int keypad)
}
/* decode 'progress' */
-static int decode_progress(int *coding, int *location, int *descr,
+static int decode_progress(struct gsm_mncc_progress *progress,
const u_int8_t *lv)
{
u_int8_t in_len = lv[0];
@@ -762,22 +870,22 @@ static int decode_progress(int *coding, int *location, int *descr,
if (in_len < 2)
return -EINVAL;
- *coding = (lv[1] & 0x60) >> 5;
- *location = lv[1] & 0x0f;
- *descr = lv[2] & 0x7f;
+ progress->coding = (lv[1] & 0x60) >> 5;
+ progress->location = lv[1] & 0x0f;
+ progress->descr = lv[2] & 0x7f;
return 0;
}
/* encode 'progress' */
-static int encode_progress(int lv_only, struct msgb *msg, int coding,
- int location, int descr)
+static int encode_progress(struct msgb *msg, int lv_only,
+ const struct gsm_mncc_progress *p)
{
u_int8_t lv[3];
lv[0] = 2;
- lv[1] = 0x80 | ((coding & 0x3) << 5) | (location & 0xf);
- lv[2] = 0x80 | (descr & 0x7f);
+ lv[1] = 0x80 | ((p->coding & 0x3) << 5) | (p->location & 0xf);
+ lv[2] = 0x80 | (p->descr & 0x7f);
if (lv_only)
msgb_lv_put(msg, lv[0], lv+1);
else
@@ -787,16 +895,18 @@ static int encode_progress(int lv_only, struct msgb *msg, int coding,
}
/* decode 'user-user' */
-static int decode_useruser(int *proto, char *info, int info_len,
+static int decode_useruser(struct gsm_mncc_useruser *uu,
const u_int8_t *lv)
{
u_int8_t in_len = lv[0];
+ char *info = uu->info;
+ int info_len = sizeof(uu->info);
int i;
if (in_len < 1)
return -EINVAL;
- *proto = lv[1];
+ uu->proto = lv[1];
for (i = 2; i <= in_len; i++) {
info_len--;
@@ -811,16 +921,17 @@ static int decode_useruser(int *proto, char *info, int info_len,
}
/* encode 'useruser' */
-static int encode_useruser(int lv_only, struct msgb *msg, int proto, char *info)
+static int encode_useruser(struct msgb *msg, int lv_only,
+ const struct gsm_mncc_useruser *uu)
{
u_int8_t lv[GSM_MAX_USERUSER + 2];
- if (strlen(info) > GSM_MAX_USERUSER)
+ if (strlen(uu->info) > GSM_MAX_USERUSER)
return -EINVAL;
- lv[0] = 1 + strlen(info);
- lv[1] = proto;
- memcpy(lv + 2, info, strlen(info));
+ lv[0] = 1 + strlen(uu->info);
+ lv[1] = uu->proto;
+ memcpy(lv + 2, uu->info, strlen(uu->info));
if (lv_only)
msgb_lv_put(msg, lv[0], lv+1);
else
@@ -830,16 +941,16 @@ static int encode_useruser(int lv_only, struct msgb *msg, int proto, char *info)
}
/* decode 'ss version' */
-static int decode_ssversion(char *info, int info_len, int *len,
+static int decode_ssversion(struct gsm_mncc_ssversion *ssv,
const u_int8_t *lv)
{
u_int8_t in_len = lv[0];
- if (in_len < 1 || in_len < info_len)
+ if (in_len < 1 || in_len < sizeof(ssv->info))
return -EINVAL;
- memcpy(info, lv + 1, in_len);
- *len = in_len;
+ memcpy(ssv->info, lv + 1, in_len);
+ ssv->len = in_len;
return 0;
}
@@ -862,27 +973,21 @@ struct msgb *gsm48_msgb_alloc(void)
int gsm48_sendmsg(struct msgb *msg)
{
- struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data;
-
if (msg->lchan) {
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data;
msg->trx = msg->lchan->ts->trx;
- if ((gh->proto_discr & GSM48_PDISC_MASK) == GSM48_PDISC_CC) {
- /* Send a 04.08 call control message, add transaction
- * ID and TI flag */
- gh->proto_discr |= msg->lchan->call.transaction_id;
-
- /* GSM 04.07 Section 11.2.3.1.3 */
- switch (msg->lchan->call.type) {
- case GSM_CT_MO:
- gh->proto_discr |= 0x80;
- break;
- case GSM_CT_MT:
- break;
- case GSM_CT_NONE:
- break;
- }
- }
+ if ((gh->proto_discr & GSM48_PDISC_MASK) == GSM48_PDISC_CC)
+ DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x) "
+ "Sending '%s' to MS.\n", msg->trx->bts->nr,
+ msg->trx->nr, msg->lchan->ts->nr,
+ gh->proto_discr & 0xf0,
+ cc_msg_names[gh->msg_type & 0x3f]);
+ else
+ DEBUGP(DCC, "(bts %d trx %d ts %d pd %02x) "
+ "Sending 0x%02x to MS.\n", msg->trx->bts->nr,
+ msg->trx->nr, msg->lchan->ts->nr,
+ gh->proto_discr, gh->msg_type);
}
msg->l3h = msg->data;
@@ -890,7 +995,6 @@ int gsm48_sendmsg(struct msgb *msg)
return rsl_data_request(msg, 0);
}
-
/* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */
int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause)
{
@@ -1080,7 +1184,7 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
{
struct gsm48_hdr *gh = msgb_l3(msg);
struct gsm48_loc_upd_req *lu;
- struct gsm_subscriber *subscr;
+ struct gsm_subscriber *subscr = NULL;
struct gsm_lchan *lchan = msg->lchan;
u_int8_t mi_type;
char mi_string[MI_SIZE];
@@ -1110,6 +1214,7 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
switch (mi_type) {
case GSM_MI_TYPE_IMSI:
+ DEBUGP(DMM, "\n");
/* we always want the IMEI, too */
rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMEI);
lchan->loc_operation->waiting_for_imei = 1;
@@ -1118,6 +1223,7 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
subscr = db_create_subscriber(mi_string);
break;
case GSM_MI_TYPE_TMSI:
+ DEBUGP(DMM, "\n");
/* we always want the IMEI, too */
rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMEI);
lchan->loc_operation->waiting_for_imei = 1;
@@ -1140,6 +1246,12 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
break;
}
+ if (!subscr) {
+ DEBUGP(DRR, "<- Can't find any subscriber for this ID\n");
+ /* FIXME: request id? close channel? */
+ return -EINVAL;
+ }
+
lchan->subscr = subscr;
/*
@@ -1179,6 +1291,13 @@ int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, u_int8_t mode)
return gsm48_sendmsg(msg);
}
+#if 0
+static u_int8_t to_bcd8(u_int8_t val)
+{
+ return ((val / 10) << 4) | (val % 10);
+}
+#endif
+
/* Section 9.2.15a */
int gsm48_tx_mm_info(struct gsm_lchan *lchan)
{
@@ -1189,6 +1308,11 @@ int gsm48_tx_mm_info(struct gsm_lchan *lchan)
u_int16_t *ptr16;
int name_len;
int i;
+#if 0
+ time_t cur_t;
+ struct tm* cur_time;
+ int tz15min;
+#endif
msg->lchan = lchan;
@@ -1226,13 +1350,9 @@ int gsm48_tx_mm_info(struct gsm_lchan *lchan)
}
#if 0
- /* move back to the top */
- time_t cur_t;
- struct tm* cur_time;
- int tz15min;
/* Section 10.5.3.9 */
cur_t = time(NULL);
- cur_time = gmtime(cur_t);
+ cur_time = gmtime(&cur_t);
ptr8 = msgb_put(msg, 8);
ptr8[0] = GSM48_IE_NET_TIME_TZ;
ptr8[1] = to_bcd8(cur_time->tm_year % 100);
@@ -1243,9 +1363,9 @@ int gsm48_tx_mm_info(struct gsm_lchan *lchan)
ptr8[6] = to_bcd8(cur_time->tm_sec);
/* 02.42: coded as BCD encoded signed value in units of 15 minutes */
tz15min = (cur_time->tm_gmtoff)/(60*15);
- ptr8[6] = to_bcd8(tz15min);
+ ptr8[7] = to_bcd8(tz15min);
if (tz15min < 0)
- ptr8[6] |= 0x80;
+ ptr8[7] |= 0x80;
#endif
return gsm48_sendmsg(msg);
@@ -1353,7 +1473,7 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg)
(struct gsm48_imsi_detach_ind *) gh->data;
u_int8_t mi_type = idi->mi[0] & GSM_MI_TYPE_MASK;
char mi_string[MI_SIZE];
- struct gsm_subscriber *subscr;
+ struct gsm_subscriber *subscr = NULL;
mi_to_string(mi_string, sizeof(mi_string), idi->mi, idi->mi_len);
DEBUGP(DMM, "IMSI DETACH INDICATION: mi_type=0x%02x MI(%s): ",
@@ -1385,8 +1505,6 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg)
} else
DEBUGP(DMM, "Unknown Subscriber ?!?\n");
- put_lchan(msg->lchan);
-
return 0;
}
@@ -1403,7 +1521,7 @@ static int gsm48_rx_mm_status(struct msgb *msg)
static int gsm0408_rcv_mm(struct msgb *msg)
{
struct gsm48_hdr *gh = msgb_l3(msg);
- int rc;
+ int rc = 0;
switch (gh->msg_type & 0xbf) {
case GSM48_MT_MM_LOC_UPD_REQUEST:
@@ -1451,7 +1569,7 @@ static int gsm48_rr_rx_pag_resp(struct msgb *msg)
u_int8_t *mi_lv = gh->data + 2 + *classmark2_lv;
u_int8_t mi_type = mi_lv[1] & GSM_MI_TYPE_MASK;
char mi_string[MI_SIZE];
- struct gsm_subscriber *subscr;
+ struct gsm_subscriber *subscr = NULL;
struct paging_signal_data sig_data;
int rc = 0;
@@ -1651,16 +1769,25 @@ int gsm48_send_rr_release(struct gsm_lchan *lchan)
* ASSIGN, then first use that TCH/F for signalling and later MODE MODIFY
* it for voice */
-static int gsm48_cc_tx_status(struct gsm_lchan *lchan)
+static void new_cc_state(struct gsm_trans *trans, int state)
+{
+ if (state > 31 || state < 0)
+ return;
+
+ DEBUGP(DCC, "new state %s -> %s\n",
+ cc_state_names[trans->state], cc_state_names[state]);
+
+ trans->state = state;
+}
+
+static int gsm48_cc_tx_status(struct gsm_trans *trans, void *arg)
{
struct msgb *msg = gsm48_msgb_alloc();
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
u_int8_t *cause, *call_state;
- gh->proto_discr = GSM48_PDISC_CC;
-
- msg->lchan = lchan;
-
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
gh->msg_type = GSM48_MT_CC_STATUS;
cause = msgb_put(msg, 3);
@@ -1688,125 +1815,160 @@ static int gsm48_tx_simple(struct gsm_lchan *lchan,
return gsm48_sendmsg(msg);
}
-/* call-back from paging the B-end of the connection */
-static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event,
- struct msgb *msg, void *_lchan, void *param)
+static void gsm48_stop_cc_timer(struct gsm_trans *trans)
{
- struct gsm_lchan *lchan = _lchan;
- struct gsm_call *remote_call = param;
- struct gsm_call *call = &lchan->call;
- int rc = 0;
-
- if (hooknum != GSM_HOOK_RR_PAGING)
- return -EINVAL;
-
- switch (event) {
- case GSM_PAGING_SUCCEEDED:
- DEBUGP(DCC, "paging succeeded!\n");
- remote_call->remote_lchan = lchan;
- call->remote_lchan = remote_call->local_lchan;
- /* send SETUP request to called party */
- rc = gsm48_cc_tx_setup(lchan, call->remote_lchan->subscr);
- if (is_ipaccess_bts(lchan->ts->trx->bts))
- rsl_ipacc_bind(lchan);
- break;
- case GSM_PAGING_EXPIRED:
- DEBUGP(DCC, "paging expired!\n");
- /* notify caller that we cannot reach called party */
- /* FIXME: correct cause, etc */
- rc = gsm48_tx_simple(remote_call->local_lchan, GSM48_PDISC_CC,
- GSM48_MT_CC_RELEASE_COMPL);
- break;
+ if (bsc_timer_pending(&trans->cc_timer)) {
+ DEBUGP(DCC, "stopping pending timer T%x\n", trans->Tcurrent);
+ bsc_del_timer(&trans->cc_timer);
+ trans->Tcurrent = 0;
}
- return rc;
}
+
+static int mncc_recvmsg(struct gsm_network *net, struct gsm_trans *trans,
+ int msg_type, struct gsm_mncc *mncc)
+{
+ struct msgb *msg;
+
+ if (trans)
+ if (trans->lchan)
+ DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x sub %s) "
+ "Sending '%s' to MNCC.\n",
+ trans->lchan->ts->trx->bts->nr,
+ trans->lchan->ts->trx->nr,
+ trans->lchan->ts->nr, trans->transaction_id,
+ (trans->subscr)?(trans->subscr->extension):"-",
+ get_mncc_name(msg_type));
+ else
+ DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
+ "Sending '%s' to MNCC.\n",
+ (trans->subscr)?(trans->subscr->extension):"-",
+ get_mncc_name(msg_type));
+ else
+ DEBUGP(DCC, "(bts - trx - ts - ti -- sub -) "
+ "Sending '%s' to MNCC.\n", get_mncc_name(msg_type));
-static int gsm48_cc_rx_status_enq(struct msgb *msg)
-{
- DEBUGP(DCC, "-> STATUS ENQ\n");
- return gsm48_cc_tx_status(msg->lchan);
+ mncc->msg_type = msg_type;
+
+ msg = msgb_alloc(sizeof(struct gsm_mncc));
+ if (!msg)
+ return -ENOMEM;
+ memcpy(msg->data, mncc, sizeof(struct gsm_mncc));
+ msgb_enqueue(&net->upqueue, msg);
+
+ return 0;
}
-static int gsm48_cc_rx_setup(struct msgb *msg)
+int mncc_release_ind(struct gsm_network *net, struct gsm_trans *trans,
+ u_int32_t callref, int location, int value)
{
- struct gsm_call *call = &msg->lchan->call;
- struct gsm48_hdr *gh = msgb_l3(msg);
- unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
- struct gsm_subscriber *called_subscr;
- char called_number[(43-2)*2 + 1] = "\0";
- struct tlv_parsed tp;
- int ret;
-
- if (call->state == GSM_CSTATE_NULL ||
- call->state == GSM_CSTATE_RELEASE_REQ)
- use_lchan(msg->lchan);
+ struct gsm_mncc rel;
- call->type = GSM_CT_MO;
- call->state = GSM_CSTATE_INITIATED;
- call->local_lchan = msg->lchan;
- call->transaction_id = gh->proto_discr & 0xf0;
-
- tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
- if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD))
- goto err;
+ memset(&rel, 0, sizeof(rel));
+ rel.callref = callref;
+ mncc_set_cause(&rel, location, value);
+ return mncc_recvmsg(net, trans, MNCC_REL_IND, &rel);
+}
- /* Parse the number that was dialed and lookup subscriber */
- decode_bcd_number(called_number, sizeof(called_number),
- TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1, 1);
+void free_trans(struct gsm_trans *trans)
+{
+ struct gsm_bts *bts;
- DEBUGP(DCC, "A -> SETUP(tid=0x%02x,number='%s')\n", call->transaction_id,
- called_number);
+ gsm48_stop_cc_timer(trans);
- called_subscr = subscr_get_by_extension(called_number);
- if (!called_subscr) {
- DEBUGP(DCC, "could not find subscriber, RELEASE\n");
- put_lchan(msg->lchan);
- return gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
- GSM48_MT_CC_RELEASE_COMPL);
- }
- if (called_subscr == msg->lchan->subscr) {
- DEBUGP(DCC, "subscriber calling himself ?!?\n");
- put_lchan(msg->lchan);
- subscr_put(called_subscr);
- return gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
- GSM48_MT_CC_RELEASE_COMPL);
+ /* send release to L4, if callref still exists */
+ if (trans->callref) {
+ /* Ressource unavailable */
+ mncc_release_ind(trans->network, trans, trans->callref,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ if (trans->state != GSM_CSTATE_NULL)
+ new_cc_state(trans, GSM_CSTATE_NULL);
}
- subscr_get(msg->lchan->subscr);
- call->called_subscr = called_subscr;
+ if (!trans->lchan && trans->subscr && trans->subscr->net) {
+ /* Stop paging on all bts' */
+ bts = NULL;
+ do {
+ bts = gsm_bts_by_lac(trans->subscr->net,
+ trans->subscr->lac, bts);
+ if (!bts)
+ break;
+ /* Stop paging */
+ paging_request_stop(bts, trans->subscr, NULL);
+ } while (1);
+ }
- /* Start paging subscriber on all BTS in LAC of subscriber */
- subscr_get_channel(called_subscr, msg->trx->bts->network, RSL_CHANNEED_TCH_F,
- setup_trig_pag_evt, call);
+ if (trans->lchan) {
+ trau_mux_unmap(&trans->lchan->ts->e1_link, trans->callref);
+ put_lchan(trans->lchan);
+ }
- /* send a CALL PROCEEDING message to the MO */
- ret = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
- GSM48_MT_CC_CALL_PROC);
+ if (trans->subscr)
+ subscr_put(trans->subscr);
- if (is_ipaccess_bts(msg->trx->bts))
- rsl_ipacc_bind(msg->lchan);
+ if (trans->state != GSM_CSTATE_NULL)
+ new_cc_state(trans, GSM_CSTATE_NULL);
- /* change TCH/F mode to voice */
- return gsm48_tx_chan_mode_modify(msg->lchan, GSM48_CMODE_SPEECH_EFR);
+ llist_del(&trans->entry);
-err:
- /* FIXME: send some kind of RELEASE */
- return 0;
+ free(trans);
}
-static int gsm48_cc_rx_alerting(struct msgb *msg)
+static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg);
+
+/* call-back from paging the B-end of the connection */
+static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event,
+ struct msgb *msg, void *_lchan, void *param)
{
- struct gsm_call *call = &msg->lchan->call;
-
- DEBUGP(DCC, "A -> ALERTING\n");
-
- /* forward ALERTING to other party */
- if (!call->remote_lchan)
- return -EIO;
+ struct gsm_lchan *lchan = _lchan;
+ struct gsm_subscriber *subscr = param;
+ struct gsm_trans *transt, *tmp;
+ struct gsm_network *net;
+
+ if (hooknum != GSM_HOOK_RR_PAGING)
+ return -EINVAL;
+
+ if (!subscr)
+ return -EINVAL;
+ net = subscr->net;
+ if (!net) {
+ DEBUGP(DCC, "Error Network not set!\n");
+ return -EINVAL;
+ }
- DEBUGP(DCC, "B <- ALERTING\n");
- return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC,
- GSM48_MT_CC_ALERTING);
+ /* check all tranactions (without lchan) for subscriber */
+ llist_for_each_entry_safe(transt, tmp, &net->trans_list, entry) {
+ if (transt->subscr != subscr || transt->lchan)
+ continue;
+ switch (event) {
+ case GSM_PAGING_SUCCEEDED:
+ if (!lchan) // paranoid
+ break;
+ DEBUGP(DCC, "Paging subscr %s succeeded!\n",
+ subscr->extension);
+ /* Assign lchan */
+ if (!transt->lchan) {
+ transt->lchan = lchan;
+ use_lchan(lchan);
+ }
+ /* send SETUP request to called party */
+ gsm48_cc_tx_setup(transt, &transt->cc_msg);
+ if (is_ipaccess_bts(lchan->ts->trx->bts))
+ rsl_ipacc_bind(lchan);
+ break;
+ case GSM_PAGING_EXPIRED:
+ DEBUGP(DCC, "Paging subscr %s expired!\n",
+ subscr->extension);
+ /* Temporarily out of order */
+ mncc_release_ind(transt->network, transt, transt->callref,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_DEST_OOO);
+ transt->callref = 0;
+ free_trans(transt);
+ break;
+ }
+ }
+ return 0;
}
/* map two ipaccess RTP streams onto each other */
@@ -1847,171 +2009,1637 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
return 0;
}
-static int gsm48_cc_rx_connect(struct msgb *msg)
+static struct gsm_trans *get_trans_ref(struct gsm_network *net, u_int32_t callref)
{
- struct gsm_call *call = &msg->lchan->call;
- int rc;
+ struct gsm_trans *trans;
+ llist_for_each_entry(trans, &net->trans_list, entry) {
+ if (trans->callref == callref)
+ return trans;
+ }
+ return NULL;
+}
- DEBUGP(DCC, "A -> CONNECT\n");
-
- rc = tch_map(msg->lchan, call->remote_lchan);
- if (rc)
+/* bridge channels of two transactions */
+static int tch_bridge(struct gsm_network *net, u_int32_t *refs)
+{
+ struct gsm_trans *trans1 = get_trans_ref(net, refs[0]);
+ struct gsm_trans *trans2 = get_trans_ref(net, refs[1]);
+
+ if (!trans1 || !trans2)
return -EIO;
-
- if (!call->remote_lchan)
+
+ if (!trans1->lchan || !trans2->lchan)
return -EIO;
- DEBUGP(DCC, "A <- CONNECT ACK\n");
- /* MT+MO: need to respond with CONNECT_ACK and pass on */
- rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
- GSM48_MT_CC_CONNECT_ACK);
+ /* through-connect channel */
+ return tch_map(trans1->lchan, trans2->lchan);
+}
+/* enable receive of channels to upqueue */
+static int tch_recv(struct gsm_network *net, struct gsm_mncc *data, int enable)
+{
+ struct gsm_trans *trans;
+
+ /* Find callref */
+ trans = get_trans_ref(net, data->callref);
+ if (!trans)
+ return -EIO;
+ if (!trans->lchan)
+ return 0;
- /* forward CONNECT to other party */
- DEBUGP(DCC, "B <- CONNECT\n");
- return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC,
- GSM48_MT_CC_CONNECT);
+ // todo IPACCESS
+ if (enable)
+ return trau_recv_lchan(trans->lchan, data->callref);
+ return trau_mux_unmap(NULL, data->callref);
}
-static int gsm48_cc_rx_disconnect(struct msgb *msg)
+/* send a frame to channel */
+static int tch_frame(struct gsm_network *net, struct gsm_trau_frame *frame)
{
- struct gsm_call *call = &msg->lchan->call;
- int rc;
+ struct gsm_trans *trans;
+ /* Find callref */
+ trans = get_trans_ref(net, frame->callref);
+ if (!trans)
+ return -EIO;
+ if (!trans->lchan)
+ return 0;
+ if (trans->lchan->type != GSM_LCHAN_TCH_F &&
+ trans->lchan->type != GSM_LCHAN_TCH_H)
+ return 0;
- /* Section 5.4.3.2 */
- DEBUGP(DCC, "A -> DISCONNECT (state->RELEASE_REQ)\n");
- call->state = GSM_CSTATE_RELEASE_REQ;
- /* FIXME: clear the network connection */
- DEBUGP(DCC, "A <- RELEASE\n");
- rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
- GSM48_MT_CC_RELEASE);
+ // todo IPACCESS
+ return trau_send_lchan(trans->lchan,
+ (struct decoded_trau_frame *)frame->data);
+}
- /*
- * FIXME: This looks wrong! We should have a single
- * place to do MMCC-REL-CNF/-REQ/-IND and then switch
- * to the NULL state and relase the call
- */
- subscr_put_channel(msg->lchan);
- /* forward DISCONNECT to other party */
- if (!call->remote_lchan)
- return -EIO;
+static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg)
+{
+ DEBUGP(DCC, "-> STATUS ENQ\n");
+ return gsm48_cc_tx_status(trans, msg);
+}
+
+static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg);
+static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg);
+
+static void gsm48_cc_timeout(void *arg)
+{
+ struct gsm_trans *trans = arg;
+ int disconnect = 0, release = 0;
+ int mo_cause = GSM48_CC_CAUSE_RECOVERY_TIMER;
+ int mo_location = GSM48_CAUSE_LOC_USER;
+ int l4_cause = GSM48_CC_CAUSE_NORMAL_UNSPEC;
+ int l4_location = GSM48_CAUSE_LOC_PRN_S_LU;
+ struct gsm_mncc mo_rel, l4_rel;
+
+ memset(&mo_rel, 0, sizeof(struct gsm_mncc));
+ mo_rel.callref = trans->callref;
+ memset(&l4_rel, 0, sizeof(struct gsm_mncc));
+ l4_rel.callref = trans->callref;
+
+ switch(trans->Tcurrent) {
+ case 0x303:
+ release = 1;
+ l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND;
+ break;
+ case 0x310:
+ disconnect = 1;
+ l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND;
+ break;
+ case 0x313:
+ disconnect = 1;
+ /* unknown, did not find it in the specs */
+ break;
+ case 0x301:
+ disconnect = 1;
+ l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND;
+ break;
+ case 0x308:
+ if (!trans->T308_second) {
+ /* restart T308 a second time */
+ gsm48_cc_tx_release(trans, &trans->cc_msg);
+ trans->T308_second = 1;
+ break; /* stay in release state */
+ }
+ free_trans(trans);
+ return;
+// release = 1;
+// l4_cause = 14;
+// break;
+ case 0x306:
+ release = 1;
+ mo_cause = trans->cc_msg.cause.value;
+ mo_location = trans->cc_msg.cause.location;
+ break;
+ case 0x323:
+ disconnect = 1;
+ break;
+ default:
+ release = 1;
+ }
+
+ if (release && trans->callref) {
+ /* process release towards layer 4 */
+ mncc_release_ind(trans->network, trans, trans->callref,
+ l4_location, l4_cause);
+ trans->callref = 0;
+ }
+
+ if (disconnect && trans->callref) {
+ /* process disconnect towards layer 4 */
+ mncc_set_cause(&l4_rel, l4_location, l4_cause);
+ mncc_recvmsg(trans->network, trans, MNCC_DISC_IND, &l4_rel);
+ }
+
+ /* process disconnect towards mobile station */
+ if (disconnect || release) {
+ mncc_set_cause(&mo_rel, mo_location, mo_cause);
+ mo_rel.cause.diag[0] = ((trans->Tcurrent & 0xf00) >> 8) + '0';
+ mo_rel.cause.diag[1] = ((trans->Tcurrent & 0x0f0) >> 4) + '0';
+ mo_rel.cause.diag[2] = (trans->Tcurrent & 0x00f) + '0';
+ mo_rel.cause.diag_len = 3;
+
+ if (disconnect)
+ gsm48_cc_tx_disconnect(trans, &mo_rel);
+ if (release)
+ gsm48_cc_tx_release(trans, &mo_rel);
+ }
- DEBUGP(DCC, "B <- DISCONNECT\n");
- return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC,
- GSM48_MT_CC_DISCONNECT);
}
-static const u_int8_t calling_bcd[] = { 0xb9, 0x32, 0x24 };
+static void gsm48_start_cc_timer(struct gsm_trans *trans, int current,
+ int sec, int micro)
+{
+ DEBUGP(DCC, "starting timer T%x with %d seconds\n", current, sec);
+ trans->cc_timer.cb = gsm48_cc_timeout;
+ trans->cc_timer.data = trans;
+ bsc_schedule_timer(&trans->cc_timer, sec, micro);
+ trans->Tcurrent = current;
+}
-int gsm48_cc_tx_setup(struct gsm_lchan *lchan,
- struct gsm_subscriber *calling_subscr)
+static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ u_int8_t msg_type = gh->msg_type & 0xbf;
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc setup;
+
+ memset(&setup, 0, sizeof(struct gsm_mncc));
+ setup.callref = trans->callref;
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+ /* emergency setup is identified by msg_type */
+ if (msg_type == GSM48_MT_CC_EMERG_SETUP)
+ setup.emergency = 1;
+
+ /* use subscriber as calling party number */
+ if (trans->subscr) {
+ setup.fields |= MNCC_F_CALLING;
+ strncpy(setup.calling.number, trans->subscr->extension,
+ sizeof(setup.calling.number)-1);
+ }
+ /* bearer capability */
+ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
+ setup.fields |= MNCC_F_BEARER_CAP;
+ decode_bearer_cap(&setup.bearer_cap,
+ TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+ }
+ /* facility */
+ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+ setup.fields |= MNCC_F_FACILITY;
+ decode_facility(&setup.facility,
+ TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+ }
+ /* called party bcd number */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) {
+ setup.fields |= MNCC_F_CALLED;
+ decode_called(&setup.called,
+ TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1);
+ }
+ /* user-user */
+ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+ setup.fields |= MNCC_F_USERUSER;
+ decode_useruser(&setup.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ }
+ /* ss-version */
+ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
+ setup.fields |= MNCC_F_SSVERSION;
+ decode_ssversion(&setup.ssversion,
+ TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
+ }
+ /* CLIR suppression */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_SUPP))
+ setup.clir.sup = 1;
+ /* CLIR invocation */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_INVOC))
+ setup.clir.inv = 1;
+ /* cc cap */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) {
+ setup.fields |= MNCC_F_CCCAP;
+ decode_cccap(&setup.cccap,
+ TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1);
+ }
+
+ if (is_ipaccess_bts(msg->trx->bts))
+ rsl_ipacc_bind(msg->lchan);
+
+ new_cc_state(trans, GSM_CSTATE_INITIATED);
+
+ /* indicate setup to MNCC */
+ mncc_recvmsg(trans->network, trans, MNCC_SETUP_IND, &setup);
+
+ return 0;
+}
+
+static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg)
{
struct msgb *msg = gsm48_msgb_alloc();
struct gsm48_hdr *gh;
- struct gsm_call *call = &lchan->call;
- u_int8_t bcd_lv[19];
+ struct gsm_mncc *setup = arg;
+ struct gsm_trans *transt;
+ u_int16_t trans_id_mask = 0;
+ int rc, i;
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
- call->type = GSM_CT_MT;
-
- call->local_lchan = msg->lchan = lchan;
- use_lchan(lchan);
+ /* transaction id must not be assigned */
+ if (trans->transaction_id != 0xff) { /* unasssigned */
+ DEBUGP(DCC, "TX Setup with assigned transaction. "
+ "This is not allowed!\n");
+ /* Temporarily out of order */
+ rc = mncc_release_ind(trans->network, trans, trans->callref,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ trans->callref = 0;
+ free_trans(trans);
+ return rc;
+ }
+
+ /* Get free transaction_id */
+ llist_for_each_entry(transt, &trans->network->trans_list, entry) {
+ /* Transaction of our lchan? */
+ if (transt->lchan == trans->lchan &&
+ transt->transaction_id != 0xff)
+ trans_id_mask |= (1 << (transt->transaction_id >> 4));
+ }
+ /* Assign free transaction ID */
+ if ((trans_id_mask & 0x007f) == 0x7f) {
+ /* no free transaction ID */
+ rc = mncc_release_ind(trans->network, trans, trans->callref,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ trans->callref = 0;
+ free_trans(trans);
+ return rc;
+ }
+ for (i = 0; i < 7; i++) {
+ if ((trans_id_mask & (1 << i)) == 0) {
+ trans->transaction_id = i << 4; /* flag = 0 */
+ break;
+ }
+ }
- gh->proto_discr = GSM48_PDISC_CC;
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
gh->msg_type = GSM48_MT_CC_SETUP;
- msgb_tv_put(msg, GSM48_IE_SIGNAL, GSM48_SIGNAL_DIALTONE);
- if (calling_subscr) {
- bcd_lv[1] = 0xb9;
- encode_bcd_number(bcd_lv, sizeof(bcd_lv), 1,
- calling_subscr->extension);
- msgb_tlv_put(msg, GSM48_IE_CALLING_BCD,
- bcd_lv[0], bcd_lv+1);
+ gsm48_start_cc_timer(trans, 0x303, GSM48_T303);
+
+ /* bearer capability */
+ if (setup->fields & MNCC_F_BEARER_CAP)
+ encode_bearer_cap(msg, 0, &setup->bearer_cap);
+ /* facility */
+ if (setup->fields & MNCC_F_FACILITY)
+ encode_facility(msg, 0, &setup->facility);
+ /* progress */
+ if (setup->fields & MNCC_F_PROGRESS)
+ encode_progress(msg, 0, &setup->progress);
+ /* calling party BCD number */
+ if (setup->fields & MNCC_F_CALLING)
+ encode_calling(msg, &setup->calling);
+ /* called party BCD number */
+ if (setup->fields & MNCC_F_CALLED)
+ encode_called(msg, &setup->called);
+ /* user-user */
+ if (setup->fields & MNCC_F_USERUSER)
+ encode_useruser(msg, 0, &setup->useruser);
+ /* redirecting party BCD number */
+ if (setup->fields & MNCC_F_REDIRECTING)
+ encode_redirecting(msg, &setup->redirecting);
+ /* signal */
+ if (setup->fields & MNCC_F_SIGNAL)
+ encode_signal(msg, setup->signal);
+
+ new_cc_state(trans, GSM_CSTATE_CALL_PRESENT);
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc call_conf;
+
+ gsm48_stop_cc_timer(trans);
+ gsm48_start_cc_timer(trans, 0x310, GSM48_T310);
+
+ memset(&call_conf, 0, sizeof(struct gsm_mncc));
+ call_conf.callref = trans->callref;
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+#if 0
+ /* repeat */
+ if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_CIR))
+ call_conf.repeat = 1;
+ if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_SEQ))
+ call_conf.repeat = 2;
+#endif
+ /* bearer capability */
+ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
+ call_conf.fields |= MNCC_F_BEARER_CAP;
+ decode_bearer_cap(&call_conf.bearer_cap,
+ TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+ }
+ /* cause */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
+ call_conf.fields |= MNCC_F_CAUSE;
+ decode_cause(&call_conf.cause,
+ TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+ }
+ /* cc cap */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) {
+ call_conf.fields |= MNCC_F_CCCAP;
+ decode_cccap(&call_conf.cccap,
+ TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1);
+ }
+
+ new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF);
+
+ return mncc_recvmsg(trans->network, trans, MNCC_CALL_CONF_IND, &call_conf);
+}
+
+static int gsm48_cc_tx_call_proc(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *proceeding = arg;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_CALL_PROC;
+
+ new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC);
+
+ /* bearer capability */
+ if (proceeding->fields & MNCC_F_BEARER_CAP)
+ encode_bearer_cap(msg, 0, &proceeding->bearer_cap);
+ /* facility */
+ if (proceeding->fields & MNCC_F_FACILITY)
+ encode_facility(msg, 0, &proceeding->facility);
+ /* progress */
+ if (proceeding->fields & MNCC_F_PROGRESS)
+ encode_progress(msg, 0, &proceeding->progress);
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc alerting;
+
+ gsm48_stop_cc_timer(trans);
+ gsm48_start_cc_timer(trans, 0x301, GSM48_T301);
+
+ memset(&alerting, 0, sizeof(struct gsm_mncc));
+ alerting.callref = trans->callref;
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+ /* facility */
+ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+ alerting.fields |= MNCC_F_FACILITY;
+ decode_facility(&alerting.facility,
+ TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+ }
+
+ /* progress */
+ if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
+ alerting.fields |= MNCC_F_PROGRESS;
+ decode_progress(&alerting.progress,
+ TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
+ }
+ /* ss-version */
+ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
+ alerting.fields |= MNCC_F_SSVERSION;
+ decode_ssversion(&alerting.ssversion,
+ TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
+ }
+
+ new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED);
+
+ return mncc_recvmsg(trans->network, trans, MNCC_ALERT_IND, &alerting);
+}
+
+static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *alerting = arg;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_ALERTING;
+
+ /* facility */
+ if (alerting->fields & MNCC_F_FACILITY)
+ encode_facility(msg, 0, &alerting->facility);
+ /* progress */
+ if (alerting->fields & MNCC_F_PROGRESS)
+ encode_progress(msg, 0, &alerting->progress);
+ /* user-user */
+ if (alerting->fields & MNCC_F_USERUSER)
+ encode_useruser(msg, 0, &alerting->useruser);
+
+ new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED);
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_tx_progress(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *progress = arg;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_PROGRESS;
+
+ /* progress */
+ encode_progress(msg, 1, &progress->progress);
+ /* user-user */
+ if (progress->fields & MNCC_F_USERUSER)
+ encode_useruser(msg, 0, &progress->useruser);
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *connect = arg;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_CONNECT;
+
+ gsm48_stop_cc_timer(trans);
+ gsm48_start_cc_timer(trans, 0x313, GSM48_T313);
+
+ /* facility */
+ if (connect->fields & MNCC_F_FACILITY)
+ encode_facility(msg, 0, &connect->facility);
+ /* progress */
+ if (connect->fields & MNCC_F_PROGRESS)
+ encode_progress(msg, 0, &connect->progress);
+ /* connected number */
+ if (connect->fields & MNCC_F_CONNECTED)
+ encode_connected(msg, &connect->connected);
+ /* user-user */
+ if (connect->fields & MNCC_F_USERUSER)
+ encode_useruser(msg, 0, &connect->useruser);
+
+ new_cc_state(trans, GSM_CSTATE_CONNECT_IND);
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc connect;
+
+ gsm48_stop_cc_timer(trans);
+
+ memset(&connect, 0, sizeof(struct gsm_mncc));
+ connect.callref = trans->callref;
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+ /* use subscriber as connected party number */
+ if (trans->subscr) {
+ connect.fields |= MNCC_F_CONNECTED;
+ strncpy(connect.connected.number, trans->subscr->extension,
+ sizeof(connect.connected.number)-1);
+ }
+ /* facility */
+ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+ connect.fields |= MNCC_F_FACILITY;
+ decode_facility(&connect.facility,
+ TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+ }
+ /* user-user */
+ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+ connect.fields |= MNCC_F_USERUSER;
+ decode_useruser(&connect.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
}
- if (lchan->subscr) {
- bcd_lv[1] = 0xb9;
- encode_bcd_number(bcd_lv, sizeof(bcd_lv), 1,
- lchan->subscr->extension);
- msgb_tlv_put(msg, GSM48_IE_CALLED_BCD,
- bcd_lv[0], bcd_lv+1);
+ /* ss-version */
+ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
+ connect.fields |= MNCC_F_SSVERSION;
+ decode_ssversion(&connect.ssversion,
+ TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
}
- DEBUGP(DCC, "B <- SETUP\n");
+ new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST);
+
+ return mncc_recvmsg(trans->network, trans, MNCC_SETUP_CNF, &connect);
+}
+
+
+static int gsm48_cc_rx_connect_ack(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm_mncc connect_ack;
+
+ gsm48_stop_cc_timer(trans);
+
+ new_cc_state(trans, GSM_CSTATE_ACTIVE);
+
+ memset(&connect_ack, 0, sizeof(struct gsm_mncc));
+ connect_ack.callref = trans->callref;
+ return mncc_recvmsg(trans->network, trans, MNCC_SETUP_COMPL_IND,
+ &connect_ack);
+}
+
+static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_CONNECT_ACK;
+
+ new_cc_state(trans, GSM_CSTATE_ACTIVE);
return gsm48_sendmsg(msg);
}
+static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc disc;
+
+ gsm48_stop_cc_timer(trans);
+
+ new_cc_state(trans, GSM_CSTATE_DISCONNECT_REQ);
+
+ memset(&disc, 0, sizeof(struct gsm_mncc));
+ disc.callref = trans->callref;
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_CAUSE, 0);
+ /* cause */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
+ disc.fields |= MNCC_F_CAUSE;
+ decode_cause(&disc.cause,
+ TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+ }
+ /* facility */
+ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+ disc.fields |= MNCC_F_FACILITY;
+ decode_facility(&disc.facility,
+ TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+ }
+ /* user-user */
+ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+ disc.fields |= MNCC_F_USERUSER;
+ decode_useruser(&disc.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ }
+ /* ss-version */
+ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
+ disc.fields |= MNCC_F_SSVERSION;
+ decode_ssversion(&disc.ssversion,
+ TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
+ }
+
+ return mncc_recvmsg(trans->network, trans, MNCC_DISC_IND, &disc);
+
+}
+
+static struct gsm_mncc_cause default_cause = {
+ .location = GSM48_CAUSE_LOC_PRN_S_LU,
+ .coding = 0,
+ .rec = 0,
+ .rec_val = 0,
+ .value = GSM48_CC_CAUSE_NORMAL_UNSPEC,
+ .diag_len = 0,
+ .diag = { 0 },
+};
+
+static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *disc = arg;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_DISCONNECT;
+
+ gsm48_stop_cc_timer(trans);
+ gsm48_start_cc_timer(trans, 0x306, GSM48_T306);
+
+ /* cause */
+ if (disc->fields & MNCC_F_CAUSE)
+ encode_cause(msg, 1, &disc->cause);
+ else
+ encode_cause(msg, 1, &default_cause);
+
+ /* facility */
+ if (disc->fields & MNCC_F_FACILITY)
+ encode_facility(msg, 0, &disc->facility);
+ /* progress */
+ if (disc->fields & MNCC_F_PROGRESS)
+ encode_progress(msg, 0, &disc->progress);
+ /* user-user */
+ if (disc->fields & MNCC_F_USERUSER)
+ encode_useruser(msg, 0, &disc->useruser);
+
+ /* store disconnect cause for T306 expiry */
+ memcpy(&trans->cc_msg, disc, sizeof(struct gsm_mncc));
+
+ new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND);
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc rel;
+ int rc;
+
+ gsm48_stop_cc_timer(trans);
+
+ memset(&rel, 0, sizeof(struct gsm_mncc));
+ rel.callref = trans->callref;
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+ /* cause */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
+ rel.fields |= MNCC_F_CAUSE;
+ decode_cause(&rel.cause,
+ TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+ }
+ /* facility */
+ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+ rel.fields |= MNCC_F_FACILITY;
+ decode_facility(&rel.facility,
+ TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+ }
+ /* user-user */
+ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+ rel.fields |= MNCC_F_USERUSER;
+ decode_useruser(&rel.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ }
+ /* ss-version */
+ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
+ rel.fields |= MNCC_F_SSVERSION;
+ decode_ssversion(&rel.ssversion,
+ TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
+ }
+
+ if (trans->state == GSM_CSTATE_RELEASE_REQ) {
+ /* release collision 5.4.5 */
+ rc = mncc_recvmsg(trans->network, trans, MNCC_REL_CNF, &rel);
+ } else {
+ rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC | trans->transaction_id,
+ GSM48_MT_CC_RELEASE_COMPL);
+ rc = mncc_recvmsg(trans->network, trans, MNCC_REL_IND, &rel);
+ }
+
+ new_cc_state(trans, GSM_CSTATE_NULL);
+
+ trans->callref = 0;
+ free_trans(trans);
+
+ return rc;
+}
+
+static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *rel = arg;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_RELEASE;
+
+ trans->callref = 0;
+
+ gsm48_stop_cc_timer(trans);
+ gsm48_start_cc_timer(trans, 0x308, GSM48_T308);
+
+ /* cause */
+ if (rel->fields & MNCC_F_CAUSE)
+ encode_cause(msg, 0, &rel->cause);
+ /* facility */
+ if (rel->fields & MNCC_F_FACILITY)
+ encode_facility(msg, 0, &rel->facility);
+ /* user-user */
+ if (rel->fields & MNCC_F_USERUSER)
+ encode_useruser(msg, 0, &rel->useruser);
+
+ trans->T308_second = 0;
+ memcpy(&trans->cc_msg, rel, sizeof(struct gsm_mncc));
+
+ if (trans->state != GSM_CSTATE_RELEASE_REQ)
+ new_cc_state(trans, GSM_CSTATE_RELEASE_REQ);
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc rel;
+ int rc = 0;
+
+ gsm48_stop_cc_timer(trans);
+
+ memset(&rel, 0, sizeof(struct gsm_mncc));
+ rel.callref = trans->callref;
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+ /* cause */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
+ rel.fields |= MNCC_F_CAUSE;
+ decode_cause(&rel.cause,
+ TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+ }
+ /* facility */
+ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+ rel.fields |= MNCC_F_FACILITY;
+ decode_facility(&rel.facility,
+ TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+ }
+ /* user-user */
+ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+ rel.fields |= MNCC_F_USERUSER;
+ decode_useruser(&rel.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ }
+ /* ss-version */
+ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
+ rel.fields |= MNCC_F_SSVERSION;
+ decode_ssversion(&rel.ssversion,
+ TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
+ }
+
+ if (trans->callref) {
+ switch (trans->state) {
+ case GSM_CSTATE_CALL_PRESENT:
+ rc = mncc_recvmsg(trans->network, trans,
+ MNCC_REJ_IND, &rel);
+ break;
+ case GSM_CSTATE_RELEASE_REQ:
+ rc = mncc_recvmsg(trans->network, trans,
+ MNCC_REL_CNF, &rel);
+ break;
+ default:
+ rc = mncc_recvmsg(trans->network, trans,
+ MNCC_REL_IND, &rel);
+ }
+ }
+
+ trans->callref = 0;
+ free_trans(trans);
+
+ return rc;
+}
+
+static int gsm48_cc_tx_release_compl(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *rel = arg;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_RELEASE_COMPL;
+
+ trans->callref = 0;
+
+ gsm48_stop_cc_timer(trans);
+
+ /* cause */
+ if (rel->fields & MNCC_F_CAUSE)
+ encode_cause(msg, 0, &rel->cause);
+ /* facility */
+ if (rel->fields & MNCC_F_FACILITY)
+ encode_facility(msg, 0, &rel->facility);
+ /* user-user */
+ if (rel->fields & MNCC_F_USERUSER)
+ encode_useruser(msg, 0, &rel->useruser);
+
+ free_trans(trans);
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_facility(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc fac;
+
+ memset(&fac, 0, sizeof(struct gsm_mncc));
+ fac.callref = trans->callref;
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_FACILITY, 0);
+ /* facility */
+ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+ fac.fields |= MNCC_F_FACILITY;
+ decode_facility(&fac.facility,
+ TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+ }
+ /* ss-version */
+ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
+ fac.fields |= MNCC_F_SSVERSION;
+ decode_ssversion(&fac.ssversion,
+ TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
+ }
+
+ return mncc_recvmsg(trans->network, trans, MNCC_FACILITY_IND, &fac);
+}
+
+static int gsm48_cc_tx_facility(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *fac = arg;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_FACILITY;
+
+ /* facility */
+ encode_facility(msg, 1, &fac->facility);
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_hold(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm_mncc hold;
+
+ memset(&hold, 0, sizeof(struct gsm_mncc));
+ hold.callref = trans->callref;
+ return mncc_recvmsg(trans->network, trans, MNCC_HOLD_IND, &hold);
+}
+
+static int gsm48_cc_tx_hold_ack(struct gsm_trans *trans, void *arg)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_HOLD_ACK;
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_tx_hold_rej(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *hold_rej = arg;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_HOLD_REJ;
+
+ /* cause */
+ if (hold_rej->fields & MNCC_F_CAUSE)
+ encode_cause(msg, 1, &hold_rej->cause);
+ else
+ encode_cause(msg, 1, &default_cause);
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_retrieve(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm_mncc retrieve;
+
+ memset(&retrieve, 0, sizeof(struct gsm_mncc));
+ retrieve.callref = trans->callref;
+ return mncc_recvmsg(trans->network, trans, MNCC_RETRIEVE_IND, &retrieve);
+}
+
+static int gsm48_cc_tx_retrieve_ack(struct gsm_trans *trans, void *arg)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_RETR_ACK;
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_tx_retrieve_rej(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *retrieve_rej = arg;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_RETR_REJ;
+
+ /* cause */
+ if (retrieve_rej->fields & MNCC_F_CAUSE)
+ encode_cause(msg, 1, &retrieve_rej->cause);
+ else
+ encode_cause(msg, 1, &default_cause);
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_start_dtmf(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc dtmf;
+
+ memset(&dtmf, 0, sizeof(struct gsm_mncc));
+ dtmf.callref = trans->callref;
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+ /* keypad facility */
+ if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY)) {
+ dtmf.fields |= MNCC_F_KEYPAD;
+ decode_keypad(&dtmf.keypad,
+ TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1);
+ }
+
+ return mncc_recvmsg(trans->network, trans, MNCC_START_DTMF_IND, &dtmf);
+}
+
+static int gsm48_cc_tx_start_dtmf_ack(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *dtmf = arg;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_START_DTMF_ACK;
+
+ /* keypad */
+ if (dtmf->fields & MNCC_F_KEYPAD)
+ encode_keypad(msg, dtmf->keypad);
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_tx_start_dtmf_rej(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *dtmf = arg;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_START_DTMF_REJ;
+
+ /* cause */
+ if (dtmf->fields & MNCC_F_CAUSE)
+ encode_cause(msg, 1, &dtmf->cause);
+ else
+ encode_cause(msg, 1, &default_cause);
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_tx_stop_dtmf_ack(struct gsm_trans *trans, void *arg)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_STOP_DTMF_ACK;
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_stop_dtmf(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm_mncc dtmf;
+
+ memset(&dtmf, 0, sizeof(struct gsm_mncc));
+ dtmf.callref = trans->callref;
+
+ return mncc_recvmsg(trans->network, trans, MNCC_STOP_DTMF_IND, &dtmf);
+}
+
+static int gsm48_cc_rx_modify(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc modify;
+
+ memset(&modify, 0, sizeof(struct gsm_mncc));
+ modify.callref = trans->callref;
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0);
+ /* bearer capability */
+ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
+ modify.fields |= MNCC_F_BEARER_CAP;
+ decode_bearer_cap(&modify.bearer_cap,
+ TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+ }
+
+ new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY);
+
+ return mncc_recvmsg(trans->network, trans, MNCC_MODIFY_IND, &modify);
+}
+
+static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *modify = arg;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_MODIFY;
+
+ gsm48_start_cc_timer(trans, 0x323, GSM48_T323);
+
+ /* bearer capability */
+ encode_bearer_cap(msg, 1, &modify->bearer_cap);
+
+ new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY);
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc modify;
+
+ gsm48_stop_cc_timer(trans);
+
+ memset(&modify, 0, sizeof(struct gsm_mncc));
+ modify.callref = trans->callref;
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0);
+ /* bearer capability */
+ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
+ modify.fields |= MNCC_F_BEARER_CAP;
+ decode_bearer_cap(&modify.bearer_cap,
+ TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+ }
+
+ new_cc_state(trans, GSM_CSTATE_ACTIVE);
+
+ return mncc_recvmsg(trans->network, trans, MNCC_MODIFY_CNF, &modify);
+}
+
+static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *modify = arg;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_MODIFY_COMPL;
+
+ /* bearer capability */
+ encode_bearer_cap(msg, 1, &modify->bearer_cap);
+
+ new_cc_state(trans, GSM_CSTATE_ACTIVE);
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc modify;
+
+ gsm48_stop_cc_timer(trans);
+
+ memset(&modify, 0, sizeof(struct gsm_mncc));
+ modify.callref = trans->callref;
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE);
+ /* bearer capability */
+ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
+ modify.fields |= GSM48_IE_BEARER_CAP;
+ decode_bearer_cap(&modify.bearer_cap,
+ TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+ }
+ /* cause */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
+ modify.fields |= MNCC_F_CAUSE;
+ decode_cause(&modify.cause,
+ TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+ }
+
+ new_cc_state(trans, GSM_CSTATE_ACTIVE);
+
+ return mncc_recvmsg(trans->network, trans, MNCC_MODIFY_REJ, &modify);
+}
+
+static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *modify = arg;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_MODIFY_REJECT;
+
+ /* bearer capability */
+ encode_bearer_cap(msg, 1, &modify->bearer_cap);
+ /* cause */
+ encode_cause(msg, 1, &modify->cause);
+
+ new_cc_state(trans, GSM_CSTATE_ACTIVE);
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *notify = arg;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_NOTIFY;
+
+ /* notify */
+ encode_notify(msg, notify->notify);
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_notify(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+// struct tlv_parsed tp;
+ struct gsm_mncc notify;
+
+ memset(&notify, 0, sizeof(struct gsm_mncc));
+ notify.callref = trans->callref;
+// tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len);
+ if (payload_len >= 1)
+ decode_notify(&notify.notify, gh->data);
+
+ return mncc_recvmsg(trans->network, trans, MNCC_NOTIFY_IND, &notify);
+}
+
+static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *user = arg;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
+ gh->msg_type = GSM48_MT_CC_USER_INFO;
+
+ /* user-user */
+ if (user->fields & MNCC_F_USERUSER)
+ encode_useruser(msg, 1, &user->useruser);
+ /* more data */
+ if (user->more)
+ encode_more(msg);
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc user;
+
+ memset(&user, 0, sizeof(struct gsm_mncc));
+ user.callref = trans->callref;
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_USER_USER, 0);
+ /* user-user */
+ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+ user.fields |= MNCC_F_USERUSER;
+ decode_useruser(&user.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ }
+ /* more data */
+ if (TLVP_PRESENT(&tp, GSM48_IE_MORE_DATA))
+ user.more = 1;
+
+ return mncc_recvmsg(trans->network, trans, MNCC_USERINFO_IND, &user);
+}
+
+static int gsm48_lchan_modify(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *mode = arg;
+
+ return gsm48_tx_chan_mode_modify(trans->lchan, mode->lchan_mode);
+}
+
+static struct downstate {
+ u_int32_t states;
+ int type;
+ int (*rout) (struct gsm_trans *trans, void *arg);
+} downstatelist[] = {
+ /* mobile originating call establishment */
+ {SBIT(GSM_CSTATE_INITIATED), /* 5.2.1.2 */
+ MNCC_CALL_PROC_REQ, gsm48_cc_tx_call_proc},
+ {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.2 | 5.2.1.5 */
+ MNCC_ALERT_REQ, gsm48_cc_tx_alerting},
+ {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.2 | 5.2.1.6 | 5.2.1.6 */
+ MNCC_SETUP_RSP, gsm48_cc_tx_connect},
+ {SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.4.2 */
+ MNCC_PROGRESS_REQ, gsm48_cc_tx_progress},
+ /* mobile terminating call establishment */
+ {SBIT(GSM_CSTATE_NULL), /* 5.2.2.1 */
+ MNCC_SETUP_REQ, gsm48_cc_tx_setup},
+ {SBIT(GSM_CSTATE_CONNECT_REQUEST),
+ MNCC_SETUP_COMPL_REQ, gsm48_cc_tx_connect_ack},
+ /* signalling during call */
+ {SBIT(GSM_CSTATE_ACTIVE),
+ MNCC_NOTIFY_REQ, gsm48_cc_tx_notify},
+ {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ),
+ MNCC_FACILITY_REQ, gsm48_cc_tx_facility},
+ {ALL_STATES,
+ MNCC_START_DTMF_RSP, gsm48_cc_tx_start_dtmf_ack},
+ {ALL_STATES,
+ MNCC_START_DTMF_REJ, gsm48_cc_tx_start_dtmf_rej},
+ {ALL_STATES,
+ MNCC_STOP_DTMF_RSP, gsm48_cc_tx_stop_dtmf_ack},
+ {SBIT(GSM_CSTATE_ACTIVE),
+ MNCC_HOLD_CNF, gsm48_cc_tx_hold_ack},
+ {SBIT(GSM_CSTATE_ACTIVE),
+ MNCC_HOLD_REJ, gsm48_cc_tx_hold_rej},
+ {SBIT(GSM_CSTATE_ACTIVE),
+ MNCC_RETRIEVE_CNF, gsm48_cc_tx_retrieve_ack},
+ {SBIT(GSM_CSTATE_ACTIVE),
+ MNCC_RETRIEVE_REJ, gsm48_cc_tx_retrieve_rej},
+ {SBIT(GSM_CSTATE_ACTIVE),
+ MNCC_MODIFY_REQ, gsm48_cc_tx_modify},
+ {SBIT(GSM_CSTATE_MO_ORIG_MODIFY),
+ MNCC_MODIFY_RSP, gsm48_cc_tx_modify_complete},
+ {SBIT(GSM_CSTATE_MO_ORIG_MODIFY),
+ MNCC_MODIFY_REJ, gsm48_cc_tx_modify_reject},
+ {SBIT(GSM_CSTATE_ACTIVE),
+ MNCC_USERINFO_REQ, gsm48_cc_tx_userinfo},
+ /* clearing */
+ {SBIT(GSM_CSTATE_INITIATED),
+ MNCC_REJ_REQ, gsm48_cc_tx_release_compl},
+ {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_DISCONNECT_IND) - SBIT(GSM_CSTATE_RELEASE_REQ) - SBIT(GSM_CSTATE_DISCONNECT_REQ), /* 5.4.4 */
+ MNCC_DISC_REQ, gsm48_cc_tx_disconnect},
+ {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */
+ MNCC_REL_REQ, gsm48_cc_tx_release},
+ /* special */
+ {ALL_STATES,
+ MNCC_LCHAN_MODIFY, gsm48_lchan_modify},
+};
+
+#define DOWNSLLEN \
+ (sizeof(downstatelist) / sizeof(struct downstate))
+
+
+int mncc_send(struct gsm_network *net, int msg_type, void *arg)
+{
+ int i, j, k, l, rc = 0;
+ struct gsm_trans *trans = NULL, *transt;
+ struct gsm_subscriber *subscr;
+ struct gsm_lchan *lchan = NULL, *lchant;
+ struct gsm_bts *bts = NULL;
+ struct gsm_bts_trx *trx;
+ struct gsm_bts_trx_ts *ts;
+ struct gsm_mncc *data = arg, rel;
+
+ /* handle special messages */
+ switch(msg_type) {
+ case MNCC_BRIDGE:
+ return tch_bridge(net, arg);
+ case MNCC_FRAME_DROP:
+ return tch_recv(net, arg, 0);
+ case MNCC_FRAME_RECV:
+ return tch_recv(net, arg, 1);
+ case GSM_TRAU_FRAME:
+ return tch_frame(net, arg);
+ }
+
+ memset(&rel, 0, sizeof(struct gsm_mncc));
+ rel.callref = data->callref;
+
+ /* Find callref */
+ trans = get_trans_ref(net, data->callref);
+
+ /* Callref unknown */
+ if (!trans) {
+ if (msg_type != MNCC_SETUP_REQ || !data->called.number[0]) {
+ DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
+ "Received '%s' from MNCC with "
+ "unknown callref %d\n", data->called.number,
+ get_mncc_name(msg_type), data->callref);
+ /* Invalid call reference */
+ return mncc_release_ind(net, NULL, data->callref,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_INVAL_TRANS_ID);
+ }
+ /* New transaction due to setup, find subscriber */
+ subscr = subscr_get_by_extension(data->called.number);
+ /* If subscriber is not found */
+ if (!subscr) {
+ DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
+ "Received '%s' from MNCC with "
+ "unknown subscriber %s\n", data->called.number,
+ get_mncc_name(msg_type), data->called.number);
+ /* Unknown subscriber */
+ return mncc_release_ind(net, NULL, data->callref,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_UNASSIGNED_NR);
+ }
+ /* If subscriber is not "attached" */
+ if (!subscr->lac) {
+ DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
+ "Received '%s' from MNCC with "
+ "detached subscriber %s\n", data->called.number,
+ get_mncc_name(msg_type), data->called.number);
+ subscr_put(subscr);
+ /* Temporarily out of order */
+ return mncc_release_ind(net, NULL, data->callref,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_DEST_OOO);
+ }
+ /* Create transaction */
+ if (!(trans = calloc(1, sizeof(struct gsm_trans)))) {
+ DEBUGP(DCC, "No memory for trans.\n");
+ subscr_put(subscr);
+ /* Ressource unavailable */
+ mncc_release_ind(net, NULL, data->callref,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ return -ENOMEM;
+ }
+ trans->callref = data->callref;
+ trans->network = net;
+ trans->transaction_id = 0xff; /* unassigned */
+ llist_add_tail(&trans->entry, &net->trans_list);
+ /* Assign subscriber to transaction */
+ trans->subscr = subscr;
+ /* Find lchan */
+ for (i = 0; i < net->num_bts; i++) {
+ bts = &net->bts[i];
+ for (j = 0; j < bts->num_trx; j++) {
+ trx = &bts->trx[j];
+ for (k = 0; k < TRX_NR_TS; k++) {
+ ts = &trx->ts[k];
+ for (l = 0; l < TS_MAX_LCHAN; l++) {
+ lchant = &ts->lchan[l];
+ if (lchant->subscr == subscr) {
+ lchan = lchant;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* If subscriber has no lchan */
+ if (!lchan) {
+ /* find transaction with this subscriber already paging */
+ llist_for_each_entry(transt, &net->trans_list, entry) {
+ /* Transaction of our lchan? */
+ if (transt == trans ||
+ transt->subscr != subscr)
+ continue;
+ DEBUGP(DCC, "(bts %d trx - ts - ti -- sub %s) "
+ "Received '%s' from MNCC with "
+ "unallocated channel, paging already "
+ "started.\n", bts->nr,
+ data->called.number,
+ get_mncc_name(msg_type));
+ return 0;
+ }
+ /* store setup informations until paging was successfull */
+ memcpy(&trans->cc_msg, data, sizeof(struct gsm_mncc));
+ /* start paging subscriber on all BTS with her location */
+ subscr->net = net;
+ bts = NULL;
+ do {
+ bts = gsm_bts_by_lac(net, subscr->lac, bts);
+ if (!bts)
+ break;
+ DEBUGP(DCC, "(bts %d trx - ts - ti -- sub %s) "
+ "Received '%s' from MNCC with "
+ "unallocated channel, paging.\n",
+ bts->nr, data->called.number,
+ get_mncc_name(msg_type));
+ /* Trigger paging */
+ paging_request(net, subscr, RSL_CHANNEED_TCH_F,
+ setup_trig_pag_evt, subscr);
+ } while (1);
+ return 0;
+ }
+ /* Assign lchan */
+ trans->lchan = lchan;
+ use_lchan(lchan);
+ }
+ lchan = trans->lchan;
+
+ /* if paging did not respond yet */
+ if (!lchan) {
+ DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
+ "Received '%s' from MNCC in paging state\n",
+ (trans->subscr)?(trans->subscr->extension):"-",
+ get_mncc_name(msg_type));
+ mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_NORM_CALL_CLEAR);
+ if (msg_type == MNCC_REL_REQ)
+ rc = mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel);
+ else
+ rc = mncc_recvmsg(net, trans, MNCC_REL_IND, &rel);
+ trans->callref = 0;
+ free_trans(trans);
+ return rc;
+ }
+
+ DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x sub %s) "
+ "Received '%s' from MNCC in state %d (%s)\n",
+ lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr,
+ trans->transaction_id,
+ (lchan->subscr)?(lchan->subscr->extension):"-",
+ get_mncc_name(msg_type), trans->state,
+ cc_state_names[trans->state]);
+
+ /* Find function for current state and message */
+ for (i = 0; i < DOWNSLLEN; i++)
+ if ((msg_type == downstatelist[i].type)
+ && ((1 << trans->state) & downstatelist[i].states))
+ break;
+ if (i == DOWNSLLEN) {
+ DEBUGP(DCC, "Message unhandled at this state.\n");
+ return 0;
+ }
+
+ rc = downstatelist[i].rout(trans, arg);
+
+ return rc;
+}
+
+
+static struct datastate {
+ u_int32_t states;
+ int type;
+ int (*rout) (struct gsm_trans *trans, struct msgb *msg);
+} datastatelist[] = {
+ /* mobile originating call establishment */
+ {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */
+ GSM48_MT_CC_SETUP, gsm48_cc_rx_setup},
+ {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */
+ GSM48_MT_CC_EMERG_SETUP, gsm48_cc_rx_setup},
+ {SBIT(GSM_CSTATE_CONNECT_IND), /* 5.2.1.2 */
+ GSM48_MT_CC_CONNECT_ACK, gsm48_cc_rx_connect_ack},
+ /* mobile terminating call establishment */
+ {SBIT(GSM_CSTATE_CALL_PRESENT), /* 5.2.2.3.2 */
+ GSM48_MT_CC_CALL_CONF, gsm48_cc_rx_call_conf},
+ {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF), /* ???? | 5.2.2.3.2 */
+ GSM48_MT_CC_ALERTING, gsm48_cc_rx_alerting},
+ {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF) | SBIT(GSM_CSTATE_CALL_RECEIVED), /* (5.2.2.6) | 5.2.2.6 | 5.2.2.6 */
+ GSM48_MT_CC_CONNECT, gsm48_cc_rx_connect},
+ /* signalling during call */
+ {ALL_STATES - SBIT(GSM_CSTATE_NULL),
+ GSM48_MT_CC_FACILITY, gsm48_cc_rx_facility},
+ {SBIT(GSM_CSTATE_ACTIVE),
+ GSM48_MT_CC_NOTIFY, gsm48_cc_rx_notify},
+ {ALL_STATES,
+ GSM48_MT_CC_START_DTMF, gsm48_cc_rx_start_dtmf},
+ {ALL_STATES,
+ GSM48_MT_CC_STOP_DTMF, gsm48_cc_rx_stop_dtmf},
+ {ALL_STATES,
+ GSM48_MT_CC_STATUS_ENQ, gsm48_cc_rx_status_enq},
+ {SBIT(GSM_CSTATE_ACTIVE),
+ GSM48_MT_CC_HOLD, gsm48_cc_rx_hold},
+ {SBIT(GSM_CSTATE_ACTIVE),
+ GSM48_MT_CC_RETR, gsm48_cc_rx_retrieve},
+ {SBIT(GSM_CSTATE_ACTIVE),
+ GSM48_MT_CC_MODIFY, gsm48_cc_rx_modify},
+ {SBIT(GSM_CSTATE_MO_TERM_MODIFY),
+ GSM48_MT_CC_MODIFY_COMPL, gsm48_cc_rx_modify_complete},
+ {SBIT(GSM_CSTATE_MO_TERM_MODIFY),
+ GSM48_MT_CC_MODIFY_REJECT, gsm48_cc_rx_modify_reject},
+ {SBIT(GSM_CSTATE_ACTIVE),
+ GSM48_MT_CC_USER_INFO, gsm48_cc_rx_userinfo},
+ /* clearing */
+ {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */
+ GSM48_MT_CC_DISCONNECT, gsm48_cc_rx_disconnect},
+ {ALL_STATES - SBIT(GSM_CSTATE_NULL), /* 5.4.4.1.2.2 */
+ GSM48_MT_CC_RELEASE, gsm48_cc_rx_release},
+ {ALL_STATES, /* 5.4.3.4 */
+ GSM48_MT_CC_RELEASE_COMPL, gsm48_cc_rx_release_compl},
+};
+
+#define DATASLLEN \
+ (sizeof(datastatelist) / sizeof(struct datastate))
+
static int gsm0408_rcv_cc(struct msgb *msg)
{
struct gsm48_hdr *gh = msgb_l3(msg);
u_int8_t msg_type = gh->msg_type & 0xbf;
- struct gsm_call *call = &msg->lchan->call;
- int rc = 0;
+ u_int8_t transaction_id = (gh->proto_discr & 0xf0) ^ 0x80; /* flip */
+ struct gsm_lchan *lchan = msg->lchan;
+ struct gsm_trans *trans = NULL, *transt;
+ struct gsm_network *net = lchan->ts->trx->bts->network;
+ int i, rc = 0;
- switch (msg_type) {
- case GSM48_MT_CC_CALL_CONF:
- /* Response to SETUP */
- DEBUGP(DCC, "-> CALL CONFIRM\n");
- /* we now need to MODIFY the channel */
- rc = gsm48_tx_chan_mode_modify(msg->lchan, GSM48_CMODE_SPEECH_EFR);
- break;
- case GSM48_MT_CC_RELEASE_COMPL:
- /* Answer from MS to RELEASE */
- DEBUGP(DCC, "-> RELEASE COMPLETE (state->NULL)\n");
- call->state = GSM_CSTATE_NULL;
- break;
- case GSM48_MT_CC_ALERTING:
- rc = gsm48_cc_rx_alerting(msg);
- break;
- case GSM48_MT_CC_CONNECT:
- rc = gsm48_cc_rx_connect(msg);
- break;
- case GSM48_MT_CC_CONNECT_ACK:
- /* MO: Answer to CONNECT */
- call->state = GSM_CSTATE_ACTIVE;
- DEBUGP(DCC, "-> CONNECT_ACK (state->ACTIVE)\n");
- break;
- case GSM48_MT_CC_RELEASE:
- DEBUGP(DCC, "-> RELEASE\n");
- DEBUGP(DCC, "<- RELEASE_COMPLETE\n");
- /* need to respond with RELEASE_COMPLETE */
- rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
- GSM48_MT_CC_RELEASE_COMPL);
- subscr_put_channel(msg->lchan);
- call->state = GSM_CSTATE_NULL;
- break;
- case GSM48_MT_CC_STATUS_ENQ:
- rc = gsm48_cc_rx_status_enq(msg);
- break;
- case GSM48_MT_CC_DISCONNECT:
- rc = gsm48_cc_rx_disconnect(msg);
- break;
- case GSM48_MT_CC_SETUP:
- /* MO: wants to establish a call */
- rc = gsm48_cc_rx_setup(msg);
- break;
- case GSM48_MT_CC_EMERG_SETUP:
- DEBUGP(DCC, "-> EMERGENCY SETUP\n");
- /* FIXME: continue with CALL_PROCEEDING, ALERTING, CONNECT, RELEASE_COMPLETE */
- break;
- case GSM48_MT_CC_HOLD:
- DEBUGP(DCC, "-> HOLD (rejecting)\n");
- rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
- GSM48_MT_CC_HOLD_REJ);
- break;
- case GSM48_MT_CC_RETR:
- DEBUGP(DCC, "-> RETR (rejecting)\n");
- rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
- GSM48_MT_CC_RETR_REJ);
- break;
- default:
- fprintf(stderr, "Unimplemented GSM 04.08 CC msg type 0x%02x\n",
- msg_type);
- break;
+ if (msg_type & 0x80) {
+ DEBUGP(DCC, "MSG 0x%2x not defined for PD error\n", msg_type);
+ return -EINVAL;
}
+
+ /* Find transaction */
+ llist_for_each_entry(transt, &net->trans_list, entry) {
+ /* Transaction of our lchan? */
+ if (transt->lchan == lchan
+ && transt->transaction_id == transaction_id) {
+ trans = transt;
+ }
+ }
+
+ DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x sub %s) "
+ "Received '%s' from MS in state %d (%s)\n",
+ lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr,
+ transaction_id, (lchan->subscr)?(lchan->subscr->extension):"-",
+ cc_msg_names[msg_type], trans?(trans->state):0,
+ cc_state_names[trans?(trans->state):0]);
+
+ /* Create transaction */
+ if (!trans) {
+ DEBUGP(DCC, "Unknown transaction ID %02x, "
+ "creating new trans.\n", transaction_id);
+ /* Create transaction */
+ if (!(trans = calloc(1, sizeof(struct gsm_trans)))) {
+ DEBUGP(DCC, "No memory for trans.\n");
+ rc = gsm48_tx_simple(msg->lchan,
+ GSM48_PDISC_CC | transaction_id,
+ GSM48_MT_CC_RELEASE_COMPL);
+ return -ENOMEM;
+ }
+ llist_add_tail(&trans->entry, &net->trans_list);
+ /* Assign transaction */
+ trans->callref = new_callref++;
+ trans->network = net;
+ trans->transaction_id = transaction_id;
+ trans->lchan = lchan;
+ use_lchan(lchan);
+ if (lchan->subscr) {
+ trans->subscr = lchan->subscr;
+ subscr_get(trans->subscr);
+ }
+ }
+
+ /* find function for current state and message */
+ for (i = 0; i < DATASLLEN; i++)
+ if ((msg_type == datastatelist[i].type)
+ && ((1 << trans->state) & datastatelist[i].states))
+ break;
+ if (i == DATASLLEN) {
+ DEBUGP(DCC, "Message unhandled at this state.\n");
+ return 0;
+ }
+
+ rc = datastatelist[i].rout(trans, msg);
return rc;
}
@@ -2050,21 +3678,6 @@ int gsm0408_rcvmsg(struct msgb *msg)
return rc;
}
-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,
-};
-
/* Section 9.1.8 / Table 9.9 */
struct chreq {
u_int8_t val;
@@ -2157,3 +3770,21 @@ enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra)
fprintf(stderr, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra);
return GSM_CHREQ_REASON_OTHER;
}
+
+/* dequeue messages to layer 4 */
+int bsc_upqueue(struct gsm_network *net)
+{
+ struct gsm_mncc *mncc;
+ struct msgb *msg;
+ int work = 0;
+
+ if (net)
+ while ((msg = msgb_dequeue(&net->upqueue))) {
+ mncc = (struct gsm_mncc *)msg->data;
+ if (net->mncc_recv)
+ net->mncc_recv(net, mncc->msg_type, mncc);
+ work = 1; /* work done */
+ }
+
+ return work;
+}
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
index a6f3e7aac..3d5820d66 100644
--- a/openbsc/src/gsm_04_11.c
+++ b/openbsc/src/gsm_04_11.c
@@ -186,9 +186,10 @@ static int gsm340_rx_tpdu(struct msgb *msg)
rc = -EIO;
goto out;
}
+ memset(address_lv, 0, sizeof(address_lv));
memcpy(address_lv, smsp, da_len_bytes);
/* mangle first byte to reflect length in bytes, not digits */
- address_lv[0] = da_len_bytes;
+ address_lv[0] = da_len_bytes - 1;
/* convert to real number */
decode_bcd_number(sms->dest_addr, sizeof(sms->dest_addr), address_lv, 1);
diff --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c
index e5e789af2..e71a6cb0d 100644
--- a/openbsc/src/gsm_data.c
+++ b/openbsc/src/gsm_data.c
@@ -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;
diff --git a/openbsc/src/ipaccess-config.c b/openbsc/src/ipaccess-config.c
index b74e46e89..cc8a6c9bc 100644
--- a/openbsc/src/ipaccess-config.c
+++ b/openbsc/src/ipaccess-config.c
@@ -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);
diff --git a/openbsc/src/mncc.c b/openbsc/src/mncc.c
new file mode 100644
index 000000000..4282aaf3c
--- /dev/null
+++ b/openbsc/src/mncc.c
@@ -0,0 +1,387 @@
+/* (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, GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ 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, GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ 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, GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
+ 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, GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
+ 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, GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
+ 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;
+}
diff --git a/openbsc/src/paging.c b/openbsc/src/paging.c
index 8f15e1640..53e51464a 100644
--- a/openbsc/src/paging.c
+++ b/openbsc/src/paging.c
@@ -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;
}
diff --git a/openbsc/src/trau_mux.c b/openbsc/src/trau_mux.c
index 96f858992..196d15fa7 100644
--- a/openbsc/src/trau_mux.c
+++ b/openbsc/src/trau_mux.c
@@ -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)
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index 44531dd50..6597e440f 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -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,