summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2010-04-04 19:42:43 +0200
committerAndreas Eversberg <jolly@eversberg.eu>2010-04-04 19:42:43 +0200
commit5118bb887a72254433b8002e653db1b10a9ac6d4 (patch)
treeef00bae760e1a06287cd332da3988a2916588cb3
parent6be5c597f9fceb5f84c0f7c19b17f684dc847ff4 (diff)
Work on layer 3: finished Call Control layer
-rw-r--r--src/host/gsm48-andreas/README15
-rw-r--r--src/host/gsm48-andreas/gsm48_cc.c1842
-rw-r--r--src/host/gsm48-andreas/gsm48_l3.h17
-rw-r--r--src/host/gsm48-andreas/gsm48_mm.c98
-rw-r--r--src/host/gsm48-andreas/issues.txt9
-rw-r--r--src/host/gsm48-andreas/transaction.h5
-rw-r--r--src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h1
-rw-r--r--src/shared/libosmocore/src/gsm48.c4
8 files changed, 1079 insertions, 912 deletions
diff --git a/src/host/gsm48-andreas/README b/src/host/gsm48-andreas/README
index 156cc4c1..d5c564f0 100644
--- a/src/host/gsm48-andreas/README
+++ b/src/host/gsm48-andreas/README
@@ -14,8 +14,7 @@ compiled - The code has been compiled, but not yet tested.
code part layer status
--------------------------------------------------------
process CC complete
-message format CC incomplete
-(messages are currently totally wrong)
+message format CC complete
state machine CC complete
debugging CC
@@ -34,17 +33,17 @@ debugging RR
idle mode PLMN selection MM(03.22) complete
idle mode cell selection MM(03.22) complete
-location update MM(03.22) -
cell selection process ??(05.08) incomplete
+interchange of 322/MM events MM incomplete
-message handling mmxx-sap incomplete
-message handling rr-sap incomplete
+message handling mmxx-sap complete
+message handling rr-sap complete
message handling (rr<->L2) rsl-sap incomplete
-message handling mm-events incomplete
+message handling mm-events complete
(messages are currently totally wrong)
-This list may not be complete.
+NOTE: This list may not be complete.
-currently working on: MM connection structure (1:1 relation to transaction).
+currently working on: Radio Ressource
diff --git a/src/host/gsm48-andreas/gsm48_cc.c b/src/host/gsm48-andreas/gsm48_cc.c
index 0f139025..51bd663f 100644
--- a/src/host/gsm48-andreas/gsm48_cc.c
+++ b/src/host/gsm48-andreas/gsm48_cc.c
@@ -19,21 +19,6 @@
*
*/
-todo: on CM service request
-establish request before sending setup (queue setup message)
-on timeout (t303): handle as required
-on establish confirm: send setup
-se establishment cause, if required
-on reject request from lower layer: handle as required
-on release request from upper layer: handle as required
-
-todo: on establish indication
-process the first message as if it is a data indication
-
-todo: handle all other MMCC primitives
-
-todo: finish message handling
-
/*
* timers
*/
@@ -59,6 +44,102 @@ static void gsm48_stop_cc_timer(struct gsm_trans *trans)
}
}
+/* timeout events of all timers */
+static void gsm48_cc_timeout(void *arg)
+{
+ struct gsm_trans *trans = arg;
+ int disconnect = 0, release = 0, abort = 1;
+ 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_USER;
+ struct gsm_mncc mo_rel, l4_rel;
+ struct msgb *nmsg;
+ struct gsm48_mmxx_hdr *mmh;
+
+ 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->cc.Tcurrent) {
+ case 0x303:
+ /* abort if connection is not already esablished */
+ if (trans->cc.state == GSM_CSTATE_MM_CONNECTION_PEND)
+ abort = 1;
+ else
+ release = 1;
+ l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND;
+ break;
+ case 0x305:
+ release = 1;
+ mo_cause = trans->cc.msg.cause.value;
+ mo_location = trans->cc.msg.cause.location;
+ break;
+ case 0x308:
+ if (!trans->cc.T308_second) {
+ /* restart T308 a second time */
+ gsm48_cc_tx_release(trans, &trans->cc.msg);
+ trans->cc.T308_second = 1;
+ break; /* stay in release state */
+ }
+ /* release MM conn, got NULL state, free trans */
+ return gsm48_rel_null_free(trans);
+ 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;
+ default:
+ release = 1;
+ }
+
+ if ((release || abort) && trans->callref) {
+ /* process release towards layer 4 */
+ mncc_release_ind(trans->ms, 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->ms, trans, MNCC_DISC_IND, &l4_rel);
+ }
+
+ /* process disconnect towards mobile station */
+ if (disconnect || release || abort) {
+ mncc_set_cause(&mo_rel, mo_location, mo_cause);
+ mo_rel.cause.diag[0] = ((trans->cc.Tcurrent & 0xf00) >> 8) + '0';
+ mo_rel.cause.diag[1] = ((trans->cc.Tcurrent & 0x0f0) >> 4) + '0';
+ mo_rel.cause.diag[2] = (trans->cc.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);
+ if (abort) {
+ struct msgb *nmsg;
+
+ /* abort MM connection */
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_REQ, trans->callref,
+ trans->transaction_id);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm48_mmxx_downmsg(nmsg);
+
+ new_cc_state(trans, GSM_CSTATE_NULL);
+
+ /* free trans (impies no callref) */
+ trans_free(trans);
+ }
+ }
+}
+
/*
* messages
*/
@@ -80,18 +161,88 @@ static int gsm48_cc_to_mm(struct msgb *msg, struct gsm_trans *trans, int msg_typ
mmh = (struct gsm48_mmxx_hdr *)msg->data;
mmh->msg_type = msg_type;
mmh->ref = trans->callref;
+ mmh->trans_id = trans->trans_id;
mmh->emergency = emergency;
/* send message to MM */
return gsm48_mmxx_downmsg(ms, msg);
}
+/* enqueue message to application (MNCC-SAP) */
+static int mncc_recvmsg(struct osmocom_ms *ms, struct gsm_trans *trans,
+ int msg_type, struct gsm_mncc *mncc)
+{
+ struct msgb *msg;
+
+ if (trans)
+ DEBUGP(DCC, "(ms %s ti %x) Sending '%s' to MNCC.\n", ms->name,
+ trans->transaction_id, get_mncc_name(msg_type));
+ else
+ DEBUGP(DCC, "(ms %s ti -) Sending '%s' to MNCC.\n", ms->name,
+ get_mncc_name(msg_type));
+
+ mncc->msg_type = msg_type;
+
+ msg = msgb_alloc(sizeof(struct gsm_mncc), "MNCC");
+ if (!msg)
+ return -ENOMEM;
+ memcpy(msg->data, mncc, sizeof(struct gsm_mncc));
+ msgb_enqueue(&ms->mncc_upqueue, msg);
+
+ return 0;
+}
+
/*
- * process handlers
+ * process handlers (misc)
*/
+/* Call Control Specific transaction release.
+ * gets called by trans_free, DO NOT CALL YOURSELF!
+ */
+void _gsm48_cc_trans_free(struct gsm_trans *trans)
+{
+ gsm48_stop_cc_timer(trans);
+
+ /* send release to L4, if callref still exists */
+ if (trans->callref) {
+ /* Ressource unavailable */
+ mncc_release_ind(trans->ms, trans, trans->callref,
+ GSM48_CAUSE_LOC_USER,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ }
+ if (trans->cc.state != GSM_CSTATE_NULL)
+ new_cc_state(trans, GSM_CSTATE_NULL);
+}
+
+/* release MM connection, got NULL state, free transaction */
+static int gsm48_rel_null_free(struct gsm_trans *trans)
+{
+ struct msgb *msg;
+
+ /* release MM connection */
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_REQ, trans->callref,
+ trans->transaction_id);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm48_mmxx_downmsg(nmsg);
+
+ new_cc_state(trans, GSM_CSTATE_NULL);
+
+ trans->callref = 0;
+ trans_free(trans);
+
+ return 0;
+}
+
+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;
+}
+
/* send release indication to upper layer */
-int mncc_release_ind(struct gsm_network *net, struct gsm_trans *trans,
+int mncc_release_ind(struct osmocom_ms *ms, struct gsm_trans *trans,
u_int32_t callref, int location, int value)
{
struct gsm_mncc rel;
@@ -99,11 +250,11 @@ int mncc_release_ind(struct gsm_network *net, struct gsm_trans *trans,
memset(&rel, 0, sizeof(rel));
rel.callref = callref;
mncc_set_cause(&rel, location, value);
- return mncc_recvmsg(net, trans, MNCC_REL_IND, &rel);
+ return mncc_recvmsg(ms, trans, MNCC_REL_IND, &rel);
}
/* sending status message in response to unknown message */
-static int gsm48_cc_tx_status(struct gsm_trans *trans, void *arg)
+static int gsm48_cc_tx_status(struct gsm_trans *trans, int cause)
{
struct msgb *nmsg;
struct gsm48_hdr *gh;
@@ -118,26 +269,49 @@ static int gsm48_cc_tx_status(struct gsm_trans *trans, void *arg)
cause = msgb_put(nmsg, 3);
cause[0] = 2;
- cause[1] = GSM48_CAUSE_CS_GSM | GSM48_CAUSE_LOC_PRN_S_LU;
- cause[2] = 0x80 | 30; /* response to status inquiry */
+ cause[1] = GSM48_CAUSE_CS_GSM | GSM48_CAUSE_LOC_USER;
+ cause[2] = 0x80 | cause;
call_state = msgb_put(nmsg, 1);
- call_state[0] = 0xc0 | 0x00;
+ call_state[0] = 0xc0 | trans->state;
return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
}
+/* reply status enquiry */
+static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg)
+{
+ return gsm48_cc_tx_status(trans, GSM48_CC_CAUSE_RESP_STATUS_INQ);
+}
-complete
-------------------------------------------------------------------------------
-incomplete
+/*
+ * process handlers (mobile originating call establish)
+ */
+
+/* on SETUP request from L4, init MM connection */
+static int gsm48_cc_init_mm(struct gsm_trans *trans, void *arg)
+{
+ struct msgb *nmsg;
+
+ /* store setup message */
+ memcpy(&trans->cc.msg, arg, sizeof(struct gsm_mncc));
+
+ new_cc_state(trans, GSM_CSTATE_MM_CONNECTION_PEND);
+
+ /* establish MM connection */
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_EST_REQ, trans->callref,
+ trans->transaction_id);
+ if (!nmsg)
+ return -ENOMEM;
+ return gsm48_mmxx_downmsg(nmsg);
+}
/* setup message from upper layer */
-static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg)
+static int gsm48_cc_tx_setup(struct gsm_trans *trans)
{
struct msgb *nmsg;
struct gsm48_hdr *gh;
- struct gsm_mncc *setup = arg;
+ struct gsm_mncc *setup = &trans->cc.msg;
int rc, trans_id;
nmsg = gsm48_l3_msgb_alloc();
@@ -150,20 +324,20 @@ static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg)
DEBUGP(DCC, "TX Setup with assigned transaction. "
"This is not allowed!\n");
/* Temporarily out of order */
- rc = mncc_release_ind(trans->subscr->net, trans, trans->callref,
- GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ rc = mncc_release_ind(trans->ms, trans, trans->callref,
+ GSM48_CAUSE_LOC_USER,
+ GSM48_CC_CAUSE_NORMAL_UNSPEC);
trans->callref = 0;
trans_free(trans);
return rc;
}
/* Get free transaction_id */
- trans_id = trans_assign_trans_id(trans->subscr, GSM48_PDISC_CC, 0);
+ trans_id = trans_assign_trans_id(trans->ms, GSM48_PDISC_CC, 0);
if (trans_id < 0) {
/* no free transaction ID */
- rc = mncc_release_ind(trans->subscr->net, trans, trans->callref,
- GSM48_CAUSE_LOC_PRN_S_LU,
+ rc = mncc_release_ind(trans->ms, trans, trans->callref,
+ GSM48_CAUSE_LOC_USER,
GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
trans->callref = 0;
trans_free(trans);
@@ -173,36 +347,209 @@ static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg)
gh->msg_type = (setup->emergency) ? GSM48_MT_CC_EMERG_SETUP : GSM48_MT_CC_SETUP;
- gsm48_start_cc_timer(trans, 0x303, GSM48_T303_MO);
+ /* actually we have to start it when CM SERVICE REQUEST has been sent,
+ * but there is no primitive for that defined. i think it is ok to
+ * do it here rather than inventing MMCC-NOTIFY-IND.
+ */
+ gsm48_start_cc_timer(trans, 0x303, GSM48_T303_MS);
+
+ if (!setup->emergency) {
+ /* bearer capability */
+ gsm48_encode_bearer_cap(nmsg, 0, &setup->bearer_cap);
+ /* facility */
+ if (setup->fields & MNCC_F_FACILITY)
+ gsm48_encode_facility(nmsg, 0, &setup->facility);
+ /* called party BCD number */
+ if (setup->fields & MNCC_F_CALLED)
+ gsm48_encode_called(nmsg, &setup->called);
+ /* user-user */
+ if (setup->fields & MNCC_F_USERUSER)
+ gsm48_encode_useruser(nmsg, 0, &setup->useruser);
+ /* ss version */
+ if (setup->fields & MNCC_F_SSVERSION)
+ gsm48_encode_ssversion(nmsg, 0, &setup->ssversion);
+ /* CLIR suppression */
+ if (setup->clir.sup)
+ gsm48_encode_clir_sup(nmsg);
+ /* CLIR invocation */
+ if (setup->clir.inv)
+ gsm48_encode_clir_inv(nmsg);
+ /* cc cap */
+ if (setup->fields & MNCC_F_CCCAP)
+ gsm48_encode_cccap(nmsg, 0, &setup->cccap);
+ }
+ /* actually MM CONNECTION PENDING */
+ new_cc_state(trans, GSM_CSTATE_CALL_INITIATED);
+
+ return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
+}
+
+/* progress is received from lower layer */
+static int gsm48_cc_rx_progress(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 progress;
+
+ memset(&progress, 0, sizeof(struct gsm_mncc));
+ progress.callref = trans->callref;
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_PROGRESS, 0);
+ /* progress */
+ if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
+ setup.fields |= MNCC_F_PROGRESS;
+ gsm48_decode_progress(&setup.progress,
+ TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
+ /* store last progress indicator */
+ trans->cc.prog_ind = setup.progress.descr;
+ }
+ /* user-user */
+ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+ disc.fields |= MNCC_F_USERUSER;
+ gsm48_decode_useruser(&disc.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ }
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_PROGRESS_IND, &progress);
+}
+
+/* call proceeding is received from lower layer */
+static int gsm48_cc_rx_call_proceeding(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_proc;
+
+ gsm48_stop_cc_timer(trans);
+
+ memset(&call_proceeding, 0, sizeof(struct gsm_mncc));
+ call_proc.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 */
- gsm48_encode_bearer_cap(nmsg, 0, &setup->bearer_cap);
+ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
+ call_proc.fields |= MNCC_F_BEARER_CAP;
+ gsm48_decode_bearer_cap(&call_proc.bearer_cap,
+ TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+ }
/* facility */
- if (setup->fields & MNCC_F_FACILITY)
- gsm48_encode_facility(nmsg, 0, &setup->facility);
- /* called party BCD number */
- if (setup->fields & MNCC_F_CALLED)
- gsm48_encode_called(nmsg, &setup->called);
+ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+ call_proc.fields |= MNCC_F_FACILITY;
+ gsm48_decode_facility(&call_proc.facility,
+ TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+ }
+
+ /* progress */
+ if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
+ call_proc.fields |= MNCC_F_PROGRESS;
+ gsm48_decode_progress(&call_proc.progress,
+ TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
+ /* store last progress indicator */
+ trans->cc.prog_ind = setup.progress.descr;
+ }
+
+ /* start T310, if last progress indicator was 1 or 2 or 64 */
+ if (trans->cc.prog_ind == 1
+ || trans->cc.prog_ind == 2
+ || trans->cc.prog_ind == 64)
+ gsm48_start_cc_timer(trans, 0x310, GSM48_T310_MS);
+
+ new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC);
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_CALL_PROC_IND,
+ &call_proc);
+}
+
+/* alerting is received by the lower layer */
+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);
+ /* no T301 in MS call control */
+
+ 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;
+ gsm48_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;
+ gsm48_decode_progress(&alerting.progress,
+ TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
+ }
/* user-user */
- if (setup->fields & MNCC_F_USERUSER)
- gsm48_encode_useruser(nmsg, 0, &setup->useruser);
- /* ss version */
- if (setup->fields & MNCC_F_SSVERSION)
- gsm48_encode_ssversion(nmsg, 0, &setup->ssversion);
- /* CLIR suppression */
- if (setup->clir.sup)
- gsm48_encode_clir_sup(nmsg);
- /* CLIR invocation */
- if (setup->clir.inv)
- gsm48_encode_clir_inv(nmsg);
- /* cc cap */
- if (setup->fields & MNCC_F_CCCAP)
- gsm48_encode_cccap(nmsg, 0, &setup->cccap);
+ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+ alerting.fields |= MNCC_F_USERUSER;
+ gsm48_decode_useruser(&alerting.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ }
- /* actually MM CONNECTION PENDING */
- new_cc_state(trans, GSM_CSTATE_CALL_INITIATED);
+ new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED);
- return gsm48_cc_to_mm(nmsg, trans, MMCC_EST_REQ);
+ return mncc_recvmsg(trans->ms, trans, MNCC_ALERT_IND,
+ &alerting);
+}
+
+/* connect is received from lower layer */
+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);
+ /* facility */
+ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+ connect.fields |= MNCC_F_FACILITY;
+ gsm48_decode_facility(&connect.facility,
+ TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+ }
+ /* connected */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CON_BCD)) {
+ connect.fields |= MNCC_F_CONNECTED;
+ gsm48_decode_connected(&alerting.connected,
+ TLVP_VAL(&tp, GSM48_IE_CON_BCD)-1);
+ }
+ /* progress */
+ if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
+ connect.fields |= MNCC_F_PROGRESS;
+ gsm48_decode_progress(&alerting.connect,
+ TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
+ }
+ /* user-user */
+ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+ connect.fields |= MNCC_F_USERUSER;
+ gsm48_decode_useruser(&connect.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ }
+
+ /* ACTIVE state is set during this: */
+ gsm48_cc_tx_connect_ack(trans, NULL);
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_SETUP_CNF, &connect);
}
/* connect ack message from upper layer */
@@ -223,6 +570,80 @@ static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg)
return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
}
+/*
+ * process handlers (mobile terminating call establish)
+ */
+
+/* setup is received from lower layer */
+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);
+
+ /* bearer capability */
+ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
+ setup.fields |= MNCC_F_BEARER_CAP;
+ gsm48_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;
+ gsm48_decode_facility(&setup.facility,
+ TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+ }
+ /* progress */
+ if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
+ setup.fields |= MNCC_F_PROGRESS;
+ gsm48_decode_progress(&setup.progress,
+ TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
+ }
+ /* signal */
+ if (TLVP_PRESENT(&tp, GSM48_IE_SIGNAL)) {
+ setup.fields |= MNCC_F_SIGNAL;
+ gsm48_decode_signal(&setup.signal,
+ TLVP_VAL(&tp, GSM48_IE_SIGNAL)-1);
+ }
+ /* calling party bcd number */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CALLING_BCD)) {
+ setup.fields |= MNCC_F_CALLING;
+ gsm48_decode_calling(&setup.calling,
+ TLVP_VAL(&tp, GSM48_IE_CALLING_BCD)-1);
+ }
+ /* called party bcd number */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) {
+ setup.fields |= MNCC_F_CALLED;
+ gsm48_decode_called(&setup.called,
+ TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1);
+ }
+ /* redirecting party bcd number */
+ if (TLVP_PRESENT(&tp, GSM48_IE_REDIR_BCD)) {
+ setup.fields |= MNCC_F_REDIRECTING;
+ gsm48_decode_redirecting(&setup.redirecting,
+ TLVP_VAL(&tp, GSM48_IE_REDIR_BCD)-1);
+ }
+ /* user-user */
+ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+ setup.fields |= MNCC_F_USERUSER;
+ gsm48_decode_useruser(&setup.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ }
+
+ new_cc_state(trans, GSM_CSTATE_CALL_PRESENT);
+
+ /* indicate setup to MNCC */
+ mncc_recvmsg(trans->ms, trans, MNCC_SETUP_IND, &setup);
+
+ return 0;
+}
+
/* call conf message from upper layer */
static int gsm48_cc_tx_call_conf(struct gsm_trans *trans, void *arg)
{
@@ -238,7 +659,6 @@ static int gsm48_cc_tx_call_conf(struct gsm_trans *trans, void *arg)
gh->msg_type = GSM48_MT_CC_CALL_CONF;
new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF);
-diesen state in MT_CALL_CONF umbenennen !!!!
/* bearer capability */
if (confirm->fields & MNCC_F_BEARER_CAP)
@@ -314,6 +734,25 @@ static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg)
return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
}
+/* connect ack is received from lower layer */
+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->ms, trans, MNCC_SETUP_COMPL_IND,
+ &connect_ack);
+}
+
+/*
+ * process handlers (during active state)
+ */
+
/* notify message from upper layer */
static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg)
{
@@ -334,6 +773,25 @@ static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg)
return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
}
+/* notify is received from lower layer */
+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 gsm_mncc notify;
+
+ memset(&notify, 0, sizeof(struct gsm_mncc));
+ notify.callref = trans->callref;
+ /* notify */
+ if (payload_len < 1) {
+ DEBUGP(DCC, "Short read of notify message error.\n");
+ return -EINVAL;
+ }
+ gsm48_decode_notify(&notify.notify, gh->data);
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_NOTIFY_IND, &notify);
+}
+
/* start dtmf message from upper layer */
static int gsm48_cc_tx_start_dtmf(struct gsm_trans *trans, void *arg)
{
@@ -354,6 +812,47 @@ static int gsm48_cc_tx_start_dtmf(struct gsm_trans *trans, void *arg)
return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
}
+/* start dtmf ack is received from lower layer */
+static int gsm48_cc_rx_start_dtmf_ack(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;
+ gsm48_decode_keypad(&dtmf.keypad,
+ TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1);
+ }
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_START_DTMF_RSP, &dtmf);
+}
+
+/* start dtmf rej is received from lower layer */
+static int gsm48_cc_rx_start_dtmf_rej(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;
+ /* cause */
+ if (payload_len < 1) {
+ DEBUGP(DCC, "Short read of dtmf reject message error.\n");
+ return -EINVAL;
+ }
+ gsm48_decode_cause(&dtmf.cause, gh->data);
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_START_DTMF_REJ, &dtmf);
+}
+
/* stop dtmf message from upper layer */
static int gsm48_cc_tx_stop_dtmf(struct gsm_trans *trans, void *arg)
{
@@ -370,23 +869,22 @@ static int gsm48_cc_tx_stop_dtmf(struct gsm_trans *trans, void *arg)
return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
}
-/* hold message from upper layer */
-static int gsm48_cc_tx_hold(struct gsm_trans *trans, void *arg)
+/* stop dtmf ack is received from lower layer */
+static int gsm48_cc_rx_stop_dtmf_ack(struct gsm_trans *trans, struct msgb *msg)
{
- struct msgb *nmsg;
- struct gsm48_hdr *gh;
-
- nmsg = gsm48_l3_msgb_alloc();
- if (!nmsg)
- return -ENOMEM;
- gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc dtmf;
- gh->msg_type = GSM48_MT_CC_HOLD;
+ memset(&dtmf, 0, sizeof(struct gsm_mncc));
+ dtmf.callref = trans->callref;
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
- return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
+ return mncc_recvmsg(trans->ms, trans, MNCC_STOP_DTMF_RSP, &dtmf);
}
-/* retrieve message from upper layer */
+/* hold message from upper layer */
static int gsm48_cc_tx_hold(struct gsm_trans *trans, void *arg)
{
struct msgb *nmsg;
@@ -397,56 +895,46 @@ static int gsm48_cc_tx_hold(struct gsm_trans *trans, void *arg)
return -ENOMEM;
gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
- gh->msg_type = GSM48_MT_CC_RETRIEVE;
+ gh->msg_type = GSM48_MT_CC_HOLD;
return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
}
-/* disconnect message from upper layer or from timer event */
-static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg)
+/* hold ack is received from lower layer */
+static int gsm48_cc_rx_hold_ack(struct gsm_trans *trans, struct msgb *msg)
{
- struct gsm_mncc *disc = arg;
- struct msgb *nmsg;
- struct gsm48_hdr *gh;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct gsm_mncc hold;
- nmsg = gsm48_l3_msgb_alloc();
- if (!nmsg)
- return -ENOMEM;
- gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+ memset(&hold, 0, sizeof(struct gsm_mncc));
+ hold.callref = trans->callref;
- gh->msg_type = GSM48_MT_CC_DISCONNECT;
+ return mncc_recvmsg(trans->ms, trans, MNCC_HOLD_CNF, &hold);
+}
- gsm48_stop_cc_timer(trans);
- gsm48_start_cc_timer(trans, 0x305, GSM48_T305_MS);
+/* hold rej is received from lower layer */
+static int gsm48_cc_rx_hold_rej(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct gsm_mncc hold;
+ memset(&hold, 0, sizeof(struct gsm_mncc));
+ hold.callref = trans->callref;
/* cause */
- if (disc->fields & MNCC_F_CAUSE)
- gsm48_encode_cause(nmsg, 1, &disc->cause);
- else
- gsm48_encode_cause(nmsg, 1, &default_cause);
-
- /* facility */
- if (disc->fields & MNCC_F_FACILITY)
- gsm48_encode_facility(nmsg, 0, &disc->facility);
- /* progress */
- if (disc->fields & MNCC_F_PROGRESS)
- gsm48_encode_progress(nmsg, 0, &disc->progress);
- /* user-user */
- if (disc->fields & MNCC_F_USERUSER)
- gsm48_encode_useruser(nmsg, 0, &disc->useruser);
- /* ss version */
- if (disc->fields & MNCC_F_SSVERSION)
- gsm48_encode_ssversion(nmsg, 0, &disc->ssversion);
-
- new_cc_state(trans, GSM_CSTATE_DISCONNECT_REQ);
+ if (payload_len < 1) {
+ DEBUGP(DCC, "Short read of hold reject message error.\n");
+ return -EINVAL;
+ }
+ gsm48_decode_cause(&hold.cause, gh->data);
- return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
+ return mncc_recvmsg(trans->ms, trans, MNCC_HOLD_REJ, &hold);
}
-/* release message from upper layer or from timer event */
-static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg)
+/* retrieve message from upper layer */
+static int gsm48_cc_tx_retrieve(struct gsm_trans *trans, void *arg)
{
- struct gsm_mncc *rel = arg;
struct msgb *nmsg;
struct gsm48_hdr *gh;
@@ -455,83 +943,42 @@ static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg)
return -ENOMEM;
gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
- gh->msg_type = GSM48_MT_CC_RELEASE;
-
- trans->callref = 0;
-
- gsm48_stop_cc_timer(trans);
- gsm48_start_cc_timer(trans, 0x308, GSM48_T308_MS);
-
- /* cause */
- if (rel->fields & MNCC_F_CAUSE)
- gsm48_encode_cause(nmsg, 0, &rel->cause);
- /* facility */
- if (rel->fields & MNCC_F_FACILITY)
- gsm48_encode_facility(nmsg, 0, &rel->facility);
- /* user-user */
- if (rel->fields & MNCC_F_USERUSER)
- gsm48_encode_useruser(nmsg, 0, &rel->useruser);
- /* ss version */
- if (rel->fields & MNCC_F_SSVERSION)
- gsm48_encode_ssversion(nmsg, 0, &rel->ssversion);
-
- trans->cc.T308_second = 0;
- memcpy(&trans->cc.msg, rel, sizeof(struct gsm_mncc));
-
- if (trans->cc.state != GSM_CSTATE_RELEASE_REQ)
- new_cc_state(trans, GSM_CSTATE_RELEASE_REQ);
+ gh->msg_type = GSM48_MT_CC_RETRIEVE;
return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
}
-/* reject message from upper layer or from timer event */
-static int gsm48_cc_tx_release_compl(struct gsm_trans *trans, void *arg)
+/* retrieve ack is received from lower layer */
+static int gsm48_cc_rx_retrieve_ack(struct gsm_trans *trans, struct msgb *msg)
{
- struct gsm_mncc *rel = arg;
- struct msgb *nmsg;
- struct gsm48_mmxx_hdr *mmh;
- struct gsm48_hdr *gh;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc retrieve;
- nmsg = gsm48_l3_msgb_alloc();
- if (!nmsg)
- return -ENOMEM;
- gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+ memset(&retrieve, 0, sizeof(struct gsm_mncc));
+ retrieve.callref = trans->callref;
- gh->msg_type = GSM48_MT_CC_RELEASE_COMPL;
+ return mncc_recvmsg(trans->ms, trans, MNCC_RETRIEVE_CNF, &retrieve);
+}
- trans->callref = 0;
-
- gsm48_stop_cc_timer(trans);
+/* retrieve rej is received from lower layer */
+static int gsm48_cc_rx_retrieve_rej(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct gsm_mncc retrieve;
+ memset(&retrieve, 0, sizeof(struct gsm_mncc));
+ retrieve.callref = trans->callref;
/* cause */
- if (rel->fields & MNCC_F_CAUSE)
- gsm48_encode_cause(nmsg, 0, &rel->cause);
- /* facility */
- if (rel->fields & MNCC_F_FACILITY)
- gsm48_encode_facility(nmsg, 0, &rel->facility);
- /* user-user */
- if (rel->fields & MNCC_F_USERUSER)
- gsm48_encode_useruser(nmsg, 0, &rel->useruser);
- /* ss version */
- if (rel->fields & MNCC_F_SSVERSION)
- gsm48_encode_ssversion(nmsg, 0, &rel->ssversion);
-
-
- gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
-
- /* release MM connection */
- nmsg = gsm48_mmxx_msgb_alloc();
- if (!nmsg)
- return -ENOMEM;
- mmh = (struct gsm48_mmxx_hdr *)nmsg->data;
- mmh->msg_type = GSM48_MMCC_REL_REQ;
- mmh->cause = **todo
- gsm48_mmxx_downmsg(ms, nmsg);
-
- new_cc_state(trans, GSM_CSTATE_NULL);
+ if (payload_len < 1) {
+ DEBUGP(DCC, "Short read of retrieve reject message error.\n");
+ return -EINVAL;
+ }
+ gsm48_decode_cause(&retrieve.cause, gh->data);
- trans->callref = 0;
- trans_free(trans);
+ return mncc_recvmsg(trans->ms, trans, MNCC_RETRIEVE_REJ, &retrieve);
}
/* facility message from upper layer or from timer event */
@@ -557,6 +1004,25 @@ static int gsm48_cc_tx_facility(struct gsm_trans *trans, void *arg)
return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
}
+/* facility is received from lower layer */
+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 gsm_mncc fac;
+
+ memset(&fac, 0, sizeof(struct gsm_mncc));
+ fac.callref = trans->callref;
+ if (payload_len < 1) {
+ DEBUGP(DCC, "Short read of facility message error.\n");
+ return -EINVAL;
+ }
+ /* facility */
+ gsm48_decode_facility(&fac.facility, gh->data);
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_FACILITY_IND, &fac);
+}
+
/* user info message from upper layer or from timer event */
static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg)
{
@@ -581,6 +1047,30 @@ static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg)
return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
}
+/* user info is received from lower layer */
+static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, void *arg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct gsm_mncc user;
+
+ memset(&user, 0, sizeof(struct gsm_mncc));
+ user.callref = trans->callref;
+ if (payload_len < 1) {
+ DEBUGP(DCC, "Short read of userinfo message error.\n");
+ return -EINVAL;
+ }
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_USERUSER, 0);
+ /* user-user */
+ gsm48_decode_useruser(&user.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ /* more data */
+ if (TLVP_PRESENT(&tp, GSM48_IE_MORE))
+ user.more = 1;
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_USERINFO_IND, &userinfo);
+}
+
/* modify message from upper layer or from timer event */
static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg)
{
@@ -605,519 +1095,254 @@ static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg)
return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
}
-/* modify complete message from upper layer or from timer event */
-static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg)
-{
- struct gsm_mncc *modify = arg;
- struct msgb *nmsg;
- struct gsm48_hdr *gh;
-
- nmsg = gsm48_l3_msgb_alloc();
- if (!nmsg)
- return -ENOMEM;
- gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
-
- gh->msg_type = GSM48_MT_CC_MODIFY_COMPL;
-
- /* bearer capability */
- gsm48_encode_bearer_cap(nmsg, 1, &modify->bearer_cap);
-
- new_cc_state(trans, GSM_CSTATE_ACTIVE);
-
- return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
-}
-
-/* modify reject message from upper layer or from timer event */
-static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg)
+/* modify complete is received from lower layer */
+static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg)
{
- struct gsm_mncc *modify = arg;
- struct msgb *nmsg;
- struct gsm48_hdr *gh;
-
- nmsg = gsm48_l3_msgb_alloc();
- if (!nmsg)
- return -ENOMEM;
- gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
-
- gh->msg_type = GSM48_MT_CC_MODIFY_REJECT;
-
- /* bearer capability */
- gsm48_encode_bearer_cap(nmsg, 1, &modify->bearer_cap);
- /* cause */
- gsm48_encode_cause(nmsg, 1, &modify->cause);
-
- new_cc_state(trans, GSM_CSTATE_ACTIVE);
-
- return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
-}
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc modify;
-/* Call Control Specific transaction release.
- * gets called by trans_free, DO NOT CALL YOURSELF!
- */
-void _gsm48_cc_trans_free(struct gsm_trans *trans)
-{
gsm48_stop_cc_timer(trans);
- /* send release to L4, if callref still exists */
- if (trans->callref) {
- /* Ressource unavailable */
- mncc_release_ind(trans->ms, trans, trans->callref,
- GSM48_CAUSE_LOC_USER,
- GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ memset(&modify, 0, sizeof(struct gsm_mncc));
+ modify.callref = trans->callref;
+ if (payload_len < 1) {
+ DEBUGP(DCC, "Short read of modify complete message error.\n");
+ return -EINVAL;
}
- if (trans->cc.state != GSM_CSTATE_NULL)
- new_cc_state(trans, GSM_CSTATE_NULL);
-}
-
-
-/* state trasitions for MNCC messages (upper layer) */
-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_NULL), /* 5.2.1 */
- MNCC_SETUP_REQ, gsm48_cc_tx_setup},
- /* mobile terminating call establishment */
- {SBIT(GSM_CSTATE_CALL_PRESENT), /* 5.2.2.3.1 */
- MNCC_CALL_CONF_REQ, gsm48_cc_tx_call_conf},
- {SBIT(GSM_CSTATE_MO_TERM_CALL_CONF), /* 5.2.2.3.2 */
- MNCC_ALERT_REQ, gsm48_cc_tx_alerting},
- {SBIT(GSM_CSTATE_MO_TERM_CALL_CONF) | SBIT(GSM_CSTATE_CALL_RECEIVED), /* 5.2.2.5 */
- MNCC_SETUP_RSP, gsm48_cc_tx_connect},
- /* signalling during call */
- {SBIT(GSM_CSTATE_ACTIVE), /* 5.3.1 */
- MNCC_NOTIFY_REQ, gsm48_cc_tx_notify},
- {ALL_STATES, /* 5.5.7.1 */
- MNCC_START_DTMF, gsm48_cc_tx_start_dtmf},
- {ALL_STATES, /* 5.5.7.3 */
- MNCC_STOP_DTMF, gsm48_cc_tx_stop_dtmf},
- {SBIT(GSM_CSTATE_ACTIVE),
- MNCC_HOLD, gsm48_cc_tx_hold},
- {SBIT(GSM_CSTATE_ACTIVE),
- MNCC_RETRIEVE, gsm48_cc_tx_retrieve},
- {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ),
- MNCC_FACILITY_REQ, gsm48_cc_tx_facility},
- {SBIT(GSM_CSTATE_ACTIVE),
- MNCC_USERINFO_REQ, gsm48_cc_tx_userinfo},
- /* clearing */
- {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_DISCONNECT_IND) - SBIT(GSM_CSTATE_RELEASE_REQ) - SBIT(GSM_CSTATE_DISCONNECT_REQ), /* 5.4.3.1 */
- MNCC_DISC_REQ, gsm48_cc_tx_disconnect},
- {SBIT(GSM_CSTATE_INITIATED),
- MNCC_REJ_REQ, gsm48_cc_tx_release_compl},
- {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* ??? */
- MNCC_REL_REQ, gsm48_cc_tx_release},
- /* modify */
- {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},
-};
-
-#define DOWNSLLEN \
- (sizeof(downstatelist) / sizeof(struct downstate))
-
-int mncc_send(struct gsm_network *net, int msg_type, void *arg)
-{
- struct gsm_mncc *data = arg, rel;
-
- /* Find callref */
- trans = trans_find_by_ref(net, data->callref);
-}
+ /* bearer capability */
+ gsm48_decode_bearer_cap(&modify.bearer_cap, gh->data);
+ new_cc_state(trans, GSM_CSTATE_ACTIVE);
-/* reply status enquiry */
-static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg)
-{
- return gsm48_cc_tx_status(trans, msg);
+ return mncc_recvmsg(trans->ms, trans, MNCC_MODIFY_CNF, &modify);
}
-/* call proceeding is received from lower layer */
-static int gsm48_cc_rx_call_proceeding(struct gsm_trans *trans, struct msgb *msg)
+/* modify reject is received from lower layer */
+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 call_proc;
+ struct gsm_mncc modify;
gsm48_stop_cc_timer(trans);
-T310 nur starten, wenn kein progress indicator mit 1, 2 oder 64 enthalten ist !!!!
-T310 stoppen, wenn eine progress message mit einem progress indicator mit 1, 2 oder 64 enthalten ist !!!!
- gsm48_start_cc_timer(trans, 0x310, GSM48_T310_MS);
- memset(&call_proceeding, 0, sizeof(struct gsm_mncc));
- call_proc.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
+ memset(&modify, 0, sizeof(struct gsm_mncc));
+ modify.callref = trans->callref;
+ if (payload_len < 1) {
+ DEBUGP(DCC, "Short read of modify reject message error.\n");
+ return -EINVAL;
+ }
+ 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)) {
call_proc.fields |= MNCC_F_BEARER_CAP;
gsm48_decode_bearer_cap(&call_proc.bearer_cap,
TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
}
- /* facility */
- if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
- call_proc.fields |= MNCC_F_FACILITY;
- gsm48_decode_facility(&call_proc.facility,
- TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
- }
-
- /* progress */
- if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
- call_proc.fields |= MNCC_F_PROGRESS;
- gsm48_decode_progress(&call_proc.progress,
- TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
+ /* cause */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
+ modify.fields |= MNCC_F_CAUSE;
+ gsm48_decode_cause(&modify.cause,
+ TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
}
- new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC);
+ new_cc_state(trans, GSM_CSTATE_ACTIVE);
- return mncc_recvmsg(trans->subscr->net, trans, MNCC_CALL_PROC_IND,
- &call_proc);
+ return mncc_recvmsg(trans->ms, trans, MNCC_MODIFY_REJ, &modify);
}
-/* alerting is received by the lower layer */
-static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg)
+/* modify is received from lower layer */
+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 alerting;
-
- gsm48_stop_cc_timer(trans);
- /* no T301 in MS call control */
-
- 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;
- gsm48_decode_facility(&alerting.facility,
- TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
- }
+ struct gsm_mncc modify;
- /* progress */
- if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
- alerting.fields |= MNCC_F_PROGRESS;
- gsm48_decode_progress(&alerting.progress,
- TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
- }
- /* user-user */
- if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
- alerting.fields |= MNCC_F_USERUSER;
- gsm48_decode_useruser(&alerting.useruser,
- TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ memset(&modify, 0, sizeof(struct gsm_mncc));
+ modify.callref = trans->callref;
+ if (payload_len < 1) {
+ DEBUGP(DCC, "Short read of modify message error.\n");
+ return -EINVAL;
}
+ /* bearer capability */
+ gsm48_decode_bearer_cap(&modify.bearer_cap, gh->data);
- new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED);
+ new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY);
- return mncc_recvmsg(trans->subscr->net, trans, MNCC_ALERT_IND,
- &alerting);
+ return mncc_recvmsg(trans->ms, trans, MNCC_MODIFY_IND, &modify);
}
-/* connect is received from lower layer */
-static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg)
+/* modify complete message from upper layer or from timer event */
+static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg)
{
- 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);
- /* facility */
- if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
- connect.fields |= MNCC_F_FACILITY;
- gsm48_decode_facility(&connect.facility,
- TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
- }
- /* connected */
- if (TLVP_PRESENT(&tp, GSM48_IE_CON_BCD)) {
- connect.fields |= MNCC_F_CONNECTED;
- gsm48_decode_connected(&alerting.connected,
- TLVP_VAL(&tp, GSM48_IE_CON_BCD)-1);
- }
- /* progress */
- if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
- connect.fields |= MNCC_F_PROGRESS;
- gsm48_decode_progress(&alerting.connect,
- TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
- }
- /* user-user */
- if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
- connect.fields |= MNCC_F_USERUSER;
- gsm48_decode_useruser(&connect.useruser,
- TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
- }
-
- /* ACTIVE state is set during this: */
- gsm48_cc_tx_connect_ack(trans, NULL);
-
- return mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_CNF, &connect);
-}
+ struct gsm_mncc *modify = arg;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
-/* setup is received from lower layer */
-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;
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
- memset(&setup, 0, sizeof(struct gsm_mncc));
- setup.callref = trans->callref;
- tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+ gh->msg_type = GSM48_MT_CC_MODIFY_COMPL;
/* bearer capability */
- if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
- setup.fields |= MNCC_F_BEARER_CAP;
- gsm48_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;
- gsm48_decode_facility(&setup.facility,
- TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
- }
- /* progress */
- if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
- setup.fields |= MNCC_F_PROGRESS;
- gsm48_decode_progress(&setup.progress,
- TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
- }
- /* signal */
- if (TLVP_PRESENT(&tp, GSM48_IE_SIGNAL)) {
- setup.fields |= MNCC_F_SIGNAL;
- gsm48_decode_signal(&setup.signal,
- TLVP_VAL(&tp, GSM48_IE_SIGNAL)-1);
- }
- /* calling party bcd number */
- if (TLVP_PRESENT(&tp, GSM48_IE_CALLING_BCD)) {
- setup.fields |= MNCC_F_CALLING;
- gsm48_decode_calling(&setup.calling,
- TLVP_VAL(&tp, GSM48_IE_CALLING_BCD)-1);
- }
- /* called party bcd number */
- if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) {
- setup.fields |= MNCC_F_CALLED;
- gsm48_decode_called(&setup.called,
- TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1);
- }
- /* redirecting party bcd number */
- if (TLVP_PRESENT(&tp, GSM48_IE_REDIR_BCD)) {
- setup.fields |= MNCC_F_REDIRECTING;
- gsm48_decode_redirecting(&setup.redirecting,
- TLVP_VAL(&tp, GSM48_IE_REDIR_BCD)-1);
- }
- /* user-user */
- if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
- setup.fields |= MNCC_F_USERUSER;
- gsm48_decode_useruser(&setup.useruser,
- TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
- }
-
- new_cc_state(trans, GSM_CSTATE_CALL_PRESENT);
+ gsm48_encode_bearer_cap(nmsg, 1, &modify->bearer_cap);
- /* indicate setup to MNCC */
- mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_IND, &setup);
+ new_cc_state(trans, GSM_CSTATE_ACTIVE);
- return 0;
+ return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
}
-/* connect ack is received from lower layer */
-static int gsm48_cc_rx_connect_ack(struct gsm_trans *trans, struct msgb *msg)
+/* modify reject message from upper layer or from timer event */
+static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg)
{
- struct gsm_mncc connect_ack;
+ struct gsm_mncc *modify = arg;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
- gsm48_stop_cc_timer(trans);
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
- new_cc_state(trans, GSM_CSTATE_ACTIVE);
-
- memset(&connect_ack, 0, sizeof(struct gsm_mncc));
- connect_ack.callref = trans->callref;
- return mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_COMPL_IND,
- &connect_ack);
-}
+ gh->msg_type = GSM48_MT_CC_MODIFY_REJECT;
-/* notify is received from lower layer */
-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 gsm_mncc notify;
+ /* bearer capability */
+ gsm48_encode_bearer_cap(nmsg, 1, &modify->bearer_cap);
+ /* cause */
+ gsm48_encode_cause(nmsg, 1, &modify->cause);
- memset(&notify, 0, sizeof(struct gsm_mncc));
- notify.callref = trans->callref;
- /* notify */
- if (payload_len < 1) {
- DEBUGP(DCC, "Short read of notify message error.\n");
- return -EINVAL;
- }
- gsm48_decode_notify(&notify.notify, gh->data);
+ new_cc_state(trans, GSM_CSTATE_ACTIVE);
- return mncc_recvmsg(trans->subscr->net, trans, MNCC_NOTIFY_IND, &notify);
+ return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
}
-/* progress is received from lower layer */
-static int gsm48_cc_rx_progress(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 progress;
-
- memset(&progress, 0, sizeof(struct gsm_mncc));
- progress.callref = trans->callref;
- tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_PROGRESS, 0);
- /* progress */
- if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
- setup.fields |= MNCC_F_PROGRESS;
- gsm48_decode_progress(&setup.progress,
- TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
- }
- /* user-user */
- if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
- disc.fields |= MNCC_F_USERUSER;
- gsm48_decode_useruser(&disc.useruser,
- TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
- }
-
- return mncc_recvmsg(trans->subscr->net, trans, MNCC_PROGRESS_IND, &progress);
-}
+/*
+ * process handlers (call clearing)
+ */
-/* start dtmf ack is received from lower layer */
-static int gsm48_cc_rx_start_dtmf_ack(struct gsm_trans *trans, struct msgb *msg)
+/* disconnect message from upper layer or from timer event */
+static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg)
{
- struct gsm48_hdr *gh = msgb_l3(msg);
- unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
- struct tlv_parsed tp;
- struct gsm_mncc dtmf;
+ struct gsm_mncc *disc = arg;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
- 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;
- gsm48_decode_keypad(&dtmf.keypad,
- TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1);
- }
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
- return mncc_recvmsg(trans->subscr->net, trans, MNCC_START_DTMF_RSP, &dtmf);
-}
+ gh->msg_type = GSM48_MT_CC_DISCONNECT;
-/* start dtmf rej is received from lower layer */
-static int gsm48_cc_rx_start_dtmf_rej(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;
+ gsm48_stop_cc_timer(trans);
+ gsm48_start_cc_timer(trans, 0x305, GSM48_T305_MS);
- memset(&dtmf, 0, sizeof(struct gsm_mncc));
- dtmf.callref = trans->callref;
/* cause */
- if (payload_len < 1) {
- DEBUGP(DCC, "Short read of dtmf reject message error.\n");
- return -EINVAL;
- }
- gsm48_decode_cause(&dtmf.cause, gh->data);
-
- return mncc_recvmsg(trans->subscr->net, trans, MNCC_START_DTMF_REJ, &dtmf);
-}
+ if (disc->fields & MNCC_F_CAUSE)
+ gsm48_encode_cause(nmsg, 1, &disc->cause);
+ else
+ gsm48_encode_cause(nmsg, 1, &default_cause);
-/* stop dtmf ack is received from lower layer */
-static int gsm48_cc_rx_stop_dtmf_ack(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;
+ /* facility */
+ if (disc->fields & MNCC_F_FACILITY)
+ gsm48_encode_facility(nmsg, 0, &disc->facility);
+ /* progress */
+ if (disc->fields & MNCC_F_PROGRESS)
+ gsm48_encode_progress(nmsg, 0, &disc->progress);
+ /* user-user */
+ if (disc->fields & MNCC_F_USERUSER)
+ gsm48_encode_useruser(nmsg, 0, &disc->useruser);
+ /* ss version */
+ if (disc->fields & MNCC_F_SSVERSION)
+ gsm48_encode_ssversion(nmsg, 0, &disc->ssversion);
- memset(&dtmf, 0, sizeof(struct gsm_mncc));
- dtmf.callref = trans->callref;
- tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+ new_cc_state(trans, GSM_CSTATE_DISCONNECT_REQ);
- return mncc_recvmsg(trans->subscr->net, trans, MNCC_STOP_DTMF_RSP, &dtmf);
+ return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
}
-/* hold ack is received from lower layer */
-static int gsm48_cc_rx_hold_ack(struct gsm_trans *trans, struct msgb *msg)
+/* release message from upper layer or from timer event */
+static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg)
{
- struct gsm48_hdr *gh = msgb_l3(msg);
- unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
- struct gsm_mncc hold;
+ struct gsm_mncc *rel = arg;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
- memset(&hold, 0, sizeof(struct gsm_mncc));
- hold.callref = trans->callref;
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
- return mncc_recvmsg(trans->subscr->net, trans, MNCC_HOLD_CNF, &hold);
-}
+ gh->msg_type = GSM48_MT_CC_RELEASE;
-/* hold rej is received from lower layer */
-static int gsm48_cc_rx_hold_rej(struct gsm_trans *trans, struct msgb *msg)
-{
- struct gsm48_hdr *gh = msgb_l3(msg);
- unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
- struct gsm_mncc hold;
+ trans->callref = 0;
+
+ gsm48_stop_cc_timer(trans);
+ gsm48_start_cc_timer(trans, 0x308, GSM48_T308_MS);
- memset(&hold, 0, sizeof(struct gsm_mncc));
- hold.callref = trans->callref;
/* cause */
- if (payload_len < 1) {
- DEBUGP(DCC, "Short read of hold reject message error.\n");
- return -EINVAL;
- }
- gsm48_decode_cause(&hold.cause, gh->data);
+ if (rel->fields & MNCC_F_CAUSE)
+ gsm48_encode_cause(nmsg, 0, &rel->cause);
+ /* facility */
+ if (rel->fields & MNCC_F_FACILITY)
+ gsm48_encode_facility(nmsg, 0, &rel->facility);
+ /* user-user */
+ if (rel->fields & MNCC_F_USERUSER)
+ gsm48_encode_useruser(nmsg, 0, &rel->useruser);
+ /* ss version */
+ if (rel->fields & MNCC_F_SSVERSION)
+ gsm48_encode_ssversion(nmsg, 0, &rel->ssversion);
+
+ trans->cc.T308_second = 0;
+ memcpy(&trans->cc.msg, rel, sizeof(struct gsm_mncc));
+
+ if (trans->cc.state != GSM_CSTATE_RELEASE_REQ)
+ new_cc_state(trans, GSM_CSTATE_RELEASE_REQ);
- return mncc_recvmsg(trans->subscr->net, trans, MNCC_HOLD_REJ, &hold);
+ return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ);
}
-/* retrieve ack is received from lower layer */
-static int gsm48_cc_rx_retrieve_ack(struct gsm_trans *trans, struct msgb *msg)
+/* reject message from upper layer or from timer event */
+static int gsm48_cc_tx_release_compl(struct gsm_trans *trans, void *arg)
{
- struct gsm48_hdr *gh = msgb_l3(msg);
- unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
- struct tlv_parsed tp;
- struct gsm_mncc retrieve;
+ struct gsm_mncc *rel = arg;
+ struct msgb *nmsg;
+ struct gsm48_mmxx_hdr *mmh;
+ struct gsm48_hdr *gh;
- memset(&retrieve, 0, sizeof(struct gsm_mncc));
- retrieve.callref = trans->callref;
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
- return mncc_recvmsg(trans->subscr->net, trans, MNCC_RETRIEVE_CNF, &retrieve);
-}
+ gh->msg_type = GSM48_MT_CC_RELEASE_COMPL;
-/* retrieve rej is received from lower layer */
-static int gsm48_cc_rx_retrieve_rej(struct gsm_trans *trans, struct msgb *msg)
-{
- struct gsm48_hdr *gh = msgb_l3(msg);
- unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
- struct gsm_mncc retrieve;
+ trans->callref = 0;
+
+ gsm48_stop_cc_timer(trans);
- memset(&retrieve, 0, sizeof(struct gsm_mncc));
- retrieve.callref = trans->callref;
/* cause */
- if (payload_len < 1) {
- DEBUGP(DCC, "Short read of retrieve reject message error.\n");
- return -EINVAL;
- }
- gsm48_decode_cause(&retrieve.cause, gh->data);
+ if (rel->fields & MNCC_F_CAUSE)
+ gsm48_encode_cause(nmsg, 0, &rel->cause);
+ /* facility */
+ if (rel->fields & MNCC_F_FACILITY)
+ gsm48_encode_facility(nmsg, 0, &rel->facility);
+ /* user-user */
+ if (rel->fields & MNCC_F_USERUSER)
+ gsm48_encode_useruser(nmsg, 0, &rel->useruser);
+ /* ss version */
+ if (rel->fields & MNCC_F_SSVERSION)
+ gsm48_encode_ssversion(nmsg, 0, &rel->ssversion);
- return mncc_recvmsg(trans->subscr->net, trans, MNCC_RETRIEVE_REJ, &retrieve);
+
+ /* release MM conn, got NULL state, free trans */
+ return gsm48_rel_null_free(trans);
}
/* disconnect is received from lower layer */
@@ -1157,8 +1382,7 @@ static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg)
/* store disconnect cause for T305 expiry */
memcpy(&trans->cc.msg, disc, sizeof(struct gsm_mncc));
- return mncc_recvmsg(trans->subscr->net, trans, MNCC_DISC_IND, &disc);
-
+ return mncc_recvmsg(trans->ms, trans, MNCC_DISC_IND, &disc);
}
/* release is received from lower layer */
@@ -1198,29 +1422,21 @@ static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg)
if (trans->cc.state == GSM_CSTATE_RELEASE_REQ) {
/* release collision 5.4.5 */
- rc = mncc_recvmsg(trans->subscr->net, trans, MNCC_REL_CNF, &rel);
+ mncc_recvmsg(trans->ms, trans, MNCC_REL_CNF, &rel);
} else {
- rc = gsm48_tx_simple(msg->lchan,
- GSM48_PDISC_CC | (trans->transaction_id << 4),
- GSM48_MT_CC_RELEASE_COMPL);
- rc = mncc_recvmsg(trans->subscr->net, trans, MNCC_REL_IND, &rel);
- }
+ struct gsm_mncc rel2;
- /* release MM connection */
- nmsg = gsm48_mmxx_msgb_alloc();
- if (!nmsg)
- return -ENOMEM;
- mmh = (struct gsm48_mmxx_hdr *)nmsg->data;
- mmh->msg_type = GSM48_MMCC_REL_REQ;
- mmh->cause = **todo
- gsm48_mmxx_downmsg(ms, nmsg);
+ /* forward cause only */
+ memcpy(&rel2, &rel, sizeof(struct gsm_mncc));
+ rel2.fields = MNCC_F_CAUSE;
+ gsm48_cc_tx_release_compl(trans, &rel);
- new_cc_state(trans, GSM_CSTATE_NULL);
-
- trans->callref = 0;
- trans_free(trans);
+ /* release indication */
+ mncc_recvmsg(trans->ms, trans, MNCC_REL_IND, &rel);
+ }
- return rc;
+ /* release MM conn, got NULL state, free trans */
+ return gsm48_rel_null_free(trans);
}
/* release complete is received from lower layer */
@@ -1261,161 +1477,119 @@ static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg)
if (trans->callref) {
switch (trans->cc.state) {
case GSM_CSTATE_CALL_PRESENT:
- rc = mncc_recvmsg(trans->subscr->net, trans,
+ mncc_recvmsg(trans->ms, trans,
MNCC_REJ_IND, &rel);
break;
case GSM_CSTATE_RELEASE_REQ:
- rc = mncc_recvmsg(trans->subscr->net, trans,
+ mncc_recvmsg(trans->ms, trans,
MNCC_REL_CNF, &rel);
- /* FIXME: in case of multiple calls, we can't simply
- * hang up here ! */
- lchan_auto_release(msg->lchan);
break;
default:
- rc = mncc_recvmsg(trans->subscr->net, trans,
+ mncc_recvmsg(trans->ms, trans,
MNCC_REL_IND, &rel);
}
}
- /* release MM connection */
- nmsg = gsm48_mmxx_msgb_alloc();
- if (!nmsg)
- return -ENOMEM;
- mmh = (struct gsm48_mmxx_hdr *)nmsg->data;
- mmh->msg_type = GSM48_MMCC_REL_REQ;
- mmh->cause = **todo
- gsm48_mmxx_downmsg(ms, nmsg);
-
- new_cc_state(trans, GSM_CSTATE_NULL);
-
- trans->callref = 0;
- trans_free(trans);
-
- return rc;
-}
-
-/* facility is received from lower layer */
-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 gsm_mncc fac;
-
- memset(&fac, 0, sizeof(struct gsm_mncc));
- fac.callref = trans->callref;
- if (payload_len < 1) {
- DEBUGP(DCC, "Short read of facility message error.\n");
- return -EINVAL;
- }
- /* facility */
- gsm48_decode_facility(&fac.facility, gh->data);
-
- return mncc_recvmsg(trans->subscr->net, trans, MNCC_FACILITY_IND, &fac);
-}
-
-/* user info is received from lower layer */
-static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, void *arg)
-{
- struct gsm48_hdr *gh = msgb_l3(msg);
- unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
- struct gsm_mncc user;
-
- memset(&user, 0, sizeof(struct gsm_mncc));
- user.callref = trans->callref;
- if (payload_len < 1) {
- DEBUGP(DCC, "Short read of userinfo message error.\n");
- return -EINVAL;
- }
- tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_USERUSER, 0);
- /* user-user */
- gsm48_decode_useruser(&user.useruser,
- TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
- /* more data */
- if (TLVP_PRESENT(&tp, GSM48_IE_MORE))
- user.more = 1;
-
- return mncc_recvmsg(trans->subscr->net, trans, MNCC_USERINFO_IND, &userinfo);
-}
-
-/* modify is received from lower layer */
-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;
- if (payload_len < 1) {
- DEBUGP(DCC, "Short read of modify message error.\n");
- return -EINVAL;
- }
- /* bearer capability */
- gsm48_decode_bearer_cap(&modify.bearer_cap, gh->data);
-
- new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY);
-
- return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_IND, &modify);
+ /* release MM conn, got NULL state, free trans */
+ return gsm48_rel_null_free(trans);
}
-/* modify complete is received from lower layer */
-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;
- if (payload_len < 1) {
- DEBUGP(DCC, "Short read of modify complete message error.\n");
- return -EINVAL;
- }
- /* bearer capability */
- gsm48_decode_bearer_cap(&modify.bearer_cap, gh->data);
+/*
+ * state machines
+ */
- new_cc_state(trans, GSM_CSTATE_ACTIVE);
+/* state trasitions for MNCC messages (upper layer) */
+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_NULL), /* 5.2.1 */
+ MNCC_SETUP_REQ, gsm48_cc_init_mm},
+ {SBIT(GSM_CSTATE_MM_CONNECTION_PEND), /* 5.2.1 */
+ MNCC_REL_REQ, gsm48_cc_abort_mm},
+ /* mobile terminating call establishment */
+ {SBIT(GSM_CSTATE_CALL_PRESENT), /* 5.2.2.3.1 */
+ MNCC_CALL_CONF_REQ, gsm48_cc_tx_call_conf},
+ {SBIT(GSM_CSTATE_MO_TERM_CALL_CONF), /* 5.2.2.3.2 */
+ MNCC_ALERT_REQ, gsm48_cc_tx_alerting},
+ {SBIT(GSM_CSTATE_MO_TERM_CALL_CONF) | SBIT(GSM_CSTATE_CALL_RECEIVED), /* 5.2.2.5 */
+ MNCC_SETUP_RSP, gsm48_cc_tx_connect},
+ /* signalling during call */
+ {SBIT(GSM_CSTATE_ACTIVE), /* 5.3.1 */
+ MNCC_NOTIFY_REQ, gsm48_cc_tx_notify},
+ {ALL_STATES, /* 5.5.7.1 */
+ MNCC_START_DTMF, gsm48_cc_tx_start_dtmf},
+ {ALL_STATES, /* 5.5.7.3 */
+ MNCC_STOP_DTMF, gsm48_cc_tx_stop_dtmf},
+ {SBIT(GSM_CSTATE_ACTIVE),
+ MNCC_HOLD, gsm48_cc_tx_hold},
+ {SBIT(GSM_CSTATE_ACTIVE),
+ MNCC_RETRIEVE, gsm48_cc_tx_retrieve},
+ {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ),
+ MNCC_FACILITY_REQ, gsm48_cc_tx_facility},
+ {SBIT(GSM_CSTATE_ACTIVE),
+ MNCC_USERINFO_REQ, gsm48_cc_tx_userinfo},
+ /* clearing */
+ {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_DISCONNECT_IND) - SBIT(GSM_CSTATE_RELEASE_REQ) - SBIT(GSM_CSTATE_DISCONNECT_REQ), /* 5.4.3.1 */
+ MNCC_DISC_REQ, gsm48_cc_tx_disconnect},
+ {SBIT(GSM_CSTATE_INITIATED),
+ MNCC_REJ_REQ, gsm48_cc_tx_release_compl},
+ {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* ??? */
+ MNCC_REL_REQ, gsm48_cc_tx_release},
+ /* modify */
+ {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},
+};
- return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_CNF, &modify);
-}
+#define DOWNSLLEN \
+ (sizeof(downstatelist) / sizeof(struct downstate))
-/* modify reject is received from lower layer */
-static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg)
+int mncc_send(struct osmocom_ms *ms, int msg_type, void *arg)
{
- struct gsm48_hdr *gh = msgb_l3(msg);
- unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
- struct tlv_parsed tp;
- struct gsm_mncc modify;
+ struct gsm_mncc *data = arg, rel;
+ int msg_type = data->msg_type;
- gsm48_stop_cc_timer(trans);
+ /* Find callref */
+ trans = trans_find_by_callref(ms, data->callref);
+
+ if (!trans) {
+ /* check for SETUP message */
+ if (msg_type != MNCC_SETUP_REQ) {
+ /* Invalid call reference */
+ return mncc_release_ind(ms, NULL, data->callref,
+ GSM48_CAUSE_LOC_USER,
+ GSM48_CC_CAUSE_INVAL_TRANS_ID);
+ }
- memset(&modify, 0, sizeof(struct gsm_mncc));
- modify.callref = trans->callref;
- if (payload_len < 1) {
- DEBUGP(DCC, "Short read of modify reject message error.\n");
- return -EINVAL;
- }
- 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)) {
- call_proc.fields |= MNCC_F_BEARER_CAP;
- gsm48_decode_bearer_cap(&call_proc.bearer_cap,
- TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
- }
- /* cause */
- if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
- modify.fields |= MNCC_F_CAUSE;
- gsm48_decode_cause(&modify.cause,
- TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+ /* Create transaction */
+ trans = trans_alloc(ms, GSM48_PDISC_CC, 0xff, data->callref);
+ if (!trans) {
+ /* No memory or whatever */
+ return mncc_release_ind(ms, NULL, data->callref,
+ GSM48_CAUSE_LOC_USER,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ }
}
- new_cc_state(trans, GSM_CSTATE_ACTIVE);
+ /* Find function for current state and message */
+ for (i = 0; i < DOWNSLLEN; i++)
+ if ((msg_type == downstatelist[i].type)
+ && ((1 << trans->cc.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 mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_REJ, &modify);
+ return rc;
}
/* state trasitions for call control messages (lower layer) */
@@ -1427,10 +1601,10 @@ static struct datastate {
/* mobile originating call establishment */
{SBIT(GSM_CSTATE_INITIATED), /* 5.2.1.3 */
GSM48_MT_CC_CALL_PROC, gsm48_cc_rx_call_proceeding},
+ {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.4.1 */
+ MNCC_PROGRESS_REQ, gsm48_cc_rx_progress},
{SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.5 */
GSM48_MT_CC_ALERTING, gsm48_cc_rx_alerting},
- {SBIT(GSM_CSTATE_MO_CALL_PROC) | SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.4.1 */
- MNCC_PROGRESS_REQ, gsm48_cc_rx_progress},
{SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.6 */
GSM48_MT_CC_CONNECT, gsm48_cc_rx_connect},
/* mobile terminating call establishment */
@@ -1480,11 +1654,16 @@ static struct datastate {
#define DATASLLEN \
(sizeof(datastatelist) / sizeof(struct datastate))
-static int gsm48_mmcc_data_ind(struct gsm_trans *trans, struct msgb *msg)
+static int gsm48_cc_data_ind(struct gsm_trans *trans, struct msgb *msg)
{
struct osmocom_ms *ms = trans->ms;
struct gsm48_hdr *gh = msgb_l3(msg);
int msg_type = gh->msg_type & 0xbf;
+ uint8_t trans_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; /* flip */
+ int msg_supported = 0; /* determine, if message is supported at all */
+
+ /* set transaction ID, if not already */
+ trans->transaction_id = trans_id;
/* pull the MMCC header */
msgb_pull(msg, sizeof(struct gsm48_mmxx_hdr));
@@ -1494,12 +1673,21 @@ static int gsm48_mmcc_data_ind(struct gsm_trans *trans, struct msgb *msg)
/* find function for current state and message */
for (i = 0; i < MMDATASLLEN; i++)
+ if (msg_type == datastatelist[i].type)
+ msg_supported = 1;
if ((msg_type == datastatelist[i].type)
&& ((1 << trans->state) & datastatelist[i].states))
break;
if (i == MMDATASLLEN) {
- DEBUGP(DMM, "Message unhandled at this state.\n");
- return 0;
+ if (msg_supported) {
+ DEBUGP(DMM, "Message unhandled at this state.\n");
+ return gsm48_cc_tx_status(trans,
+ GSM48_REJECT_MSG_TYPE_NOT_COMPATIBLE);
+ } else {
+ DEBUGP(DMM, "Message not supported.\n");
+ return gsm48_cc_tx_status(trans,
+ GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED);
+ }
}
rc = datastatelist[i].rout(trans, msg);
@@ -1507,106 +1695,54 @@ static int gsm48_mmcc_data_ind(struct gsm_trans *trans, struct msgb *msg)
return rc;
}
-/* state trasitions for call control messages (lower layer) */
-static struct mmxxstate {
- u_int32_t states;
- int type;
- int (*rout) (struct gsm_trans *trans, struct msgb *msg);
-} mmxxstatelist[] = {
-what?: {SBIT(GSM_CSTATE_ACTIVE),
- GSM48_MT_CC_MODIFY, gsm48_cc_rx_modify},
-};
-
-
-/* timeout events of all timers */
-static void gsm48_cc_timeout(void *arg)
+/* receive message from MM layer */
+static int gsm48_rcv_cc(struct osmocom_ms *ms, struct msgb *msg)
{
- struct gsm_trans *trans = arg;
- int disconnect = 0, release = 0;
- int mo_cause = GSM48_CC_CAUSE_RECOVERY_TIMER;
-** openbsc must use LOC_PRIV_LOC_U on protocol side or network (maybe some cfg option)
-** auch beim LCR für ms-mode anpassen !!!!
- int mo_location = GSM48_CAUSE_LOC_PRN_S_LU;
- int l4_cause = GSM48_CC_CAUSE_NORMAL_UNSPEC;
- int l4_location = GSM48_CAUSE_LOC_PRN_S_LU;
- struct gsm_mncc mo_rel, l4_rel;
- struct msgb *nmsg;
- struct gsm48_mmxx_hdr *mmh;
+ struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
+ int msg_type = mmh->msg_type;
+ int rc = 0;
- 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;
+ trans = trans_find_by_callref(ms, mmh->ref);
+ if (!trans) {
+ trans = trans_alloc(ms, GSM48_PDISC_CC, mmh->trans_id,
+ mmh->ref);
+ if (!trans)
+ return -ENOMEM;
+ }
- switch(trans->cc.Tcurrent) {
- case 0x303:
- release = 1;
- l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND;
+ switch (msg_type) {
+ case GSM48_MMCC_EST_IND:
+ /* data included */
+ rc = gsm48_cc_data_ind(trans, msg);
break;
- case 0x305:
- release = 1;
- mo_cause = trans->cc.msg.cause.value;
- mo_location = trans->cc.msg.cause.location;
+ case GSM48_MMCC_EST_CNF:
+ /* send setup after confirm */
+ if (trans->state == GSM_CSTATE_MM_CONNECTION_PEND)
+ rc = gsm48_cc_tx_setup(trans);
+ else
+ DEBUGP(DMM, "Oops, MMCC-EST-CONF in state %d?\n",
+ trans.cc->state);
break;
- case 0x308:
- if (!trans->cc.T308_second) {
- /* restart T308 a second time */
- gsm48_cc_tx_release(trans, &trans->cc.msg);
- trans->cc.T308_second = 1;
- break; /* stay in release state */
- }
- /* release MM connection */
- nmsg = gsm48_mmxx_msgb_alloc();
- if (!nmsg)
- return -ENOMEM;
- mmh = (struct gsm48_mmxx_hdr *)nmsg->data;
- mmh->msg_type = GSM48_MMCC_REL_REQ;
- mmh->cause = **todo
- gsm48_mmxx_downmsg(ms, nmsg);
-
- new_cc_state(trans, GSM_CSTATE_NULL);
-
+ case GSM48_MMCC_ERR_IND: /* no supporting re-establishment */
+ case GSM48_MMCC_REL_IND:
+ /* release L4, release transaction */
+ mncc_release_ind(trans->ms, trans, trans->callref,
+ GSM48_CAUSE_LOC_USER, GSM48_CC_CAUSE_NORMAL_UNSPEC);
trans->callref = 0;
- trans_free(trans);
- return;
- case 0x310:
- disconnect = 1;
- l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND;
+ free_trans(trans);
break;
- case 0x313:
- disconnect = 1;
- /* unknown, did not find it in the specs */
+ case GSM48_MMCC_DATA_IND:
+ rc = gsm48_cc_data_ind(trans, msg);
+ break;
+ case GSM48_MMCC_UNIT_DATA_IND:
+ break;
+ case GSM48_MMCC_SYNC_IND:
break;
default:
- release = 1;
- }
-
- if (release && trans->callref) {
- /* process release towards layer 4 */
- mncc_release_ind(trans->subscr->net, 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->subscr->net, 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->cc.Tcurrent & 0xf00) >> 8) + '0';
- mo_rel.cause.diag[1] = ((trans->cc.Tcurrent & 0x0f0) >> 4) + '0';
- mo_rel.cause.diag[2] = (trans->cc.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(DMM, "Message unhandled.\n");
+ rc = -ENOTSUP;
}
+ return rc;
}
diff --git a/src/host/gsm48-andreas/gsm48_l3.h b/src/host/gsm48-andreas/gsm48_l3.h
index 605e7430..a2bc5dae 100644
--- a/src/host/gsm48-andreas/gsm48_l3.h
+++ b/src/host/gsm48-andreas/gsm48_l3.h
@@ -190,23 +190,6 @@ struct gsm48_mmxx_hdr {
#define GSM48_MM_SST_RX_VGCS_NORMAL 9
#define GSM48_MM_SST_RX_VGCS_LIMITED 10
-/* GSM 04.08 5.1.2.2 */
-#define GSM48_CC_ST_NULL 0
-#define GSM48_CC_ST_INITIATED 1
-#define GSM48_CC_ST_MO_CALL_PROC 3
-#define GSM48_CC_ST_CALL_DELIVERED 4
-#define GSM48_CC_ST_CALL_PRESENT 6
-#define GSM48_CC_ST_CALL_RECEIVED 7
-#define GSM48_CC_ST_CONNECT_REQUEST 8
-#define GSM48_CC_ST_MO_TERM_CALL_CONF 9
-#define GSM48_CC_ST_ACTIVE 10
-#define GSM48_CC_ST_DISCONNECT_REQ 12
-#define GSM48_CC_ST_DISCONNECT_IND 12
-#define GSM48_CC_ST_RELEASE_REQ 19
-#define GSM48_CC_ST_MO_ORIG_MODIFY 26
-#define GSM48_CC_ST_MO_TERM_MODIFY 27
-#define GSM48_CC_ST_CONNECT_IND 28
-
/* MM events */
#define GSM48_MM_EVENT_NEW_LAI 1
#define GSM48_MM_EVENT_TIMEOUT_T3211 2
diff --git a/src/host/gsm48-andreas/gsm48_mm.c b/src/host/gsm48-andreas/gsm48_mm.c
index e6ecc560..1bb270a4 100644
--- a/src/host/gsm48-andreas/gsm48_mm.c
+++ b/src/host/gsm48-andreas/gsm48_mm.c
@@ -459,7 +459,7 @@ const char *get_mmxx_name(int value)
}
/* allocate GSM 04.08 message (MMxx-SAP) */
-static struct msgb *gsm48_mmxx_msgb_alloc(int msg_type, uint32_t ref)
+static struct msgb *gsm48_mmxx_msgb_alloc(int msg_type, uint32_t ref, uint8_t trans_id)
{
struct msgb *msg;
struct gsm48_mmxx_hdr *mmh;
@@ -472,6 +472,7 @@ static struct msgb *gsm48_mmxx_msgb_alloc(int msg_type, uint32_t ref)
mmh = (struct gsm48_mmxx_hdr *)msgb_put(msg, sizeof(*mmh));
mmh->msg_type = msg_type;
mmh->ref = ref;
+ mmh->trans_id = trans_id;
return msg;
}
@@ -509,14 +510,42 @@ int gsm48_mmevent_msg(struct osmocom_ms *ms, struct msgb *msg)
msgb_enqueue(&mm->mmevent_queue, msg);
}
+/* dequeue messages (MMxx-SAP) */
+int gsm48_mm_dequeue(struct osmocom_ms *ms)
+{
+ struct gsm_mmlayer *rr = &ms->rrlayer;
+ struct msgb *msg;
+ int work = 0;
+
+ while ((msg = msgb_dequeue(&rr->rr_upqueue))) {
+ switch (msg->msg_type & GSM48_MMXX_MASK) {
+ case GSM48_MMCC_CLASS:
+ gsm48_rcv_cc(ms, msg);
+ break;
+#if 0
+ case GSM48_MMSS_CLASS:
+ gsm48_rcv_ss(ms, msg);
+ break;
+ case GSM48_MMSMS_CLASS:
+ gsm48_rcv_sms(ms, msg);
+ break;
+#endif
+ }
+ free_msgb(msg);
+ work = 1; /* work done */
+ }
+
+ return work;
+}
+
/* dequeue messages (RR-SAP) */
int gsm48_rr_dequeue(struct osmocom_ms *ms)
{
- struct gsm_mmlayer *mm = &ms->mmlayer;
+ struct gsm_mmlayer *rr = &ms->rrlayer;
struct msgb *msg;
int work = 0;
- while ((msg = msgb_dequeue(&mm->mm_upqueue))) {
+ while ((msg = msgb_dequeue(&rr->rr_upqueue))) {
/* msg is freed there */
gsm48_rcv_rr(ms, msg);
work = 1; /* work done */
@@ -526,7 +555,7 @@ int gsm48_rr_dequeue(struct osmocom_ms *ms)
}
/* dequeue MM event messages */
-int gsm48_rr_dequeue(struct osmocom_ms *ms)
+int gsm48_mmevent_dequeue(struct osmocom_ms *ms)
{
struct gsm_mmlayer *mm = &ms->mmlayer;
struct msgb *msg;
@@ -1037,7 +1066,7 @@ static int gsm48_mm_imsi_detach_release(struct osmocom_ms *ms, void *arg)
stop_mm_t3230(mm);
/* release all connections */
- gsm48_mm_release_mm_conn(ms, 1, 16);
+ gsm48_mm_release_mm_conn(ms, 1, 16, 0);
/* wait for release of RR */
if (!s->att_allowed) {
@@ -1066,7 +1095,8 @@ static int gsm48_mm_imsi_detach_delay(struct osmocom_ms *ms, void *arg)
}
/* support function to release pending/all ongoing MM connections */
-static int gsm48_mm_release_mm_conn(struct osmocom_ms *ms, int abort_any, uint8_t cause)
+static int gsm48_mm_release_mm_conn(struct osmocom_ms *ms, int abort_any,
+ uint8_t cause, int error)
{
struct gsm48_mmlayer *mm = &ms->mm;
struct gsm48_mm_conn *conn, *conn2;
@@ -1083,15 +1113,18 @@ static int gsm48_mm_release_mm_conn(struct osmocom_ms *ms, int abort_any, uint8_
switch(conn->protocol) {
case GSM48_PDISC_CC:
nmsg = gsm48_mmxx_msgb_alloc(
- GSM48_MMCC_REL_IND, conn->ref);
+ error ? GSM48_MMCC_ERR_IND
+ : GSM48_MMCC_REL_IND, conn->ref, conn->trans_id);
break;
case GSM48_PDISC_SS:
nmsg = gsm48_mmxx_msgb_alloc(
- GSM48_MMSS_REL_IND, conn->ref);
+ error ? GSM48_MMSS_ERR_IND
+ : GSM48_MMSS_REL_IND, conn->ref, conn->trans_id);
break;
case GSM48_PDISC_SMS:
nmsg = gsm48_mmxx_msgb_alloc(
- GSM48_MMSMS_REL_IND, conn->ref);
+ error ? GSM48_MMSMS_ERR_IND
+ : GSM48_MMSMS_REL_IND, conn->ref, conn->trans_id);
break;
}
if (!nmsg) {
@@ -1136,7 +1169,7 @@ static int gsm48_mm_rx_abort(struct osmocom_ms *ms, struct msgb *msg)
stop_mm_t3230(mm);
DEBUGP(DMM, "Abort (cause #%d) while MM connection is established.\n", reject_cause);
- gsm48_mm_release_mm_conn(ms, 1, 16);
+ gsm48_mm_release_mm_conn(ms, 1, 16, 0);
}
if (reject_cause == GSM48_REJECT_ILLEGAL_ME) {
@@ -1800,7 +1833,7 @@ static int gsm48_mm_rx_cm_service_rej(struct osmocom_ms *ms, struct msgb *msg)
}
/* release MM connection(s) */
- gsm48_mm_release_mm_conn(ms, abort_any, 16);
+ gsm48_mm_release_mm_conn(ms, abort_any, 16, 0);
/* state depends on the existance of remaining MM connections */
if (llist_empty(&mm->mm_conn))
@@ -1844,13 +1877,13 @@ static int gsm48_mm_init_mm(struct osmocom_ms *ms, struct mgsb *msg, int rr_prim
nmsg = NULL;
switch(msg_type) {
case GSM48_MMCC_EST_REQ:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_IND, mmh->ref);
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_IND, mmh->ref, mmh->trans_id);
break;
case GSM48_MMSS_EST_REQ:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_REL_IND, mmh->ref);
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_REL_IND, mmh->ref, mmh->trans_id);
break;
case GSM48_MMSMS_EST_REQ:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_REL_IND, mmh->ref);
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_REL_IND, mmh->ref, mmh->trans_id);
break;
}
if (!nmsg)
@@ -2044,13 +2077,13 @@ static int gsm48_mm_init_mm_reject(struct osmocom_ms *ms, struct msgb *msg)
nmsg = NULL;
switch(msg_type) {
case GSM48_MMCC_EST_REQ:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_REQ, mmh->ref);
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_REQ, mmh->ref, mmh->trans_id);
break;
case GSM48_MMSS_EST_REQ:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_REL_REQ, mmh->ref);
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_REL_REQ, mmh->ref, mmh->trans_id);
break;
case GSM48_MMSMS_EST_REQ:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_REL_REQ, mmh->ref);
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_REL_REQ, mmh->ref, mmh->trans_id);
break;
}
if (!nmsg)
@@ -2094,13 +2127,13 @@ static int gsm48_mm_conn_go_dedic(struct osmocom_ms *ms, struct msgb *msg)
nmsg = NULL;
switch(conn_found->protocol) {
case GSM48_PDISC_CC:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_EST_CNF, conn->ref);
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_EST_CNF, conn->ref, conn->trans_id);
break;
case GSM48_PDISC_SS:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_EST_CNF, conn->ref);
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_EST_CNF, conn->ref, conn->trans_id);
break;
case GSM48_PDISC_SMS:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_EST_CNF, conn->ref);
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_EST_CNF, conn->ref, conn->trans_id);
break;
}
if (!nmsg)
@@ -2138,7 +2171,7 @@ static int gsm48_mm_sync_ind_active(struct osmocom_ms *ms, struct msgb *msg)
nmsg = NULL;
switch(conn_found->protocol) {
case GSM48_PDISC_CC:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_SYNC_IND, conn->ref);
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_SYNC_IND, conn->ref, conn->trans_id);
break;
}
if (!nmsg)
@@ -2163,7 +2196,7 @@ static int gsm48_mm_abort_mm_con(struct osmocom_ms *ms, struct msgb *msg)
stop_mm_t3230(mm);
/* release all connections */
- gsm48_mm_release_mm_conn(ms, 1, 16);
+ gsm48_mm_release_mm_conn(ms, 1, 16, 1);
/* return to MM IDLE */
return gsm48_mm_return_idle(ms);
@@ -2175,7 +2208,7 @@ static int gsm48_mm_timeout_mm_con(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_mmlayer *mm = &ms->mmlayer;
/* release pending connection */
- gsm48_mm_release_mm_conn(ms, 0, 102);
+ gsm48_mm_release_mm_conn(ms, 0, 102, 0);
/* state depends on the existance of remaining MM connections */
if (llist_empty(&mm->mm_conn))
@@ -2202,7 +2235,7 @@ static int gsm48_mm_data(struct osmocom_ms *ms, struct msgb *msg)
int msg_type = mmh->msg_type;
/* get connection, if not exist (anymore), release */
- conn = mm_conn_by_id(mm, mmh->ref);
+ conn = mm_conn_by_ref(mm, mmh->ref);
if (!conn) {
switch(msg_type & GSM48_MMXX_MASK) {
case GSM48_MMCC_CLASS:
@@ -2234,7 +2267,7 @@ static int gsm48_mm_release_active(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
/* get connection, if not exist (anymore), release */
- conn = mm_conn_by_id(mm, mmh->ref);
+ conn = mm_conn_by_ref(mm, mmh->ref);
if (conn)
mm_conn_free(conn);
@@ -2254,7 +2287,7 @@ static int gsm48_mm_release_wait_add(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
/* get connection, if not exist (anymore), release */
- conn = mm_conn_by_id(mm, mmh->ref);
+ conn = mm_conn_by_ref(mm, mmh->ref);
if (conn)
mm_conn_free(conn);
@@ -2268,7 +2301,7 @@ static int gsm48_mm_release_wait_active(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
/* get connection, if not exist (anymore), release */
- conn = mm_conn_by_id(mm, mmh->ref);
+ conn = mm_conn_by_ref(mm, mmh->ref);
if (conn)
mm_conn_free(conn);
@@ -2293,7 +2326,7 @@ static int gsm48_mm_release_wait_active(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
/* get connection, if not exist (anymore), release */
- conn = mm_conn_by_id(mm, mmh->ref);
+ conn = mm_conn_by_ref(mm, mmh->ref);
if (conn)
mm_conn_free(conn);
@@ -2440,6 +2473,11 @@ int gsm48_mmxx_downmsg(struct osmocom_ms *ms, struct msgb msg)
struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
int msg_type = mmh->msg_type;
+ /* keep up to date with the transaction ID */
+ conn = mm_conn_by_ref(mm, mmh->ref);
+ if (conn)
+ conn->trans_id = mmh->trans_id;
+
DEBUGP(DMM, "(ms %s) Received '%s' event in state %s", ms->name,
get_mmevent_name(msg_type), gsm48_mm_state_names[mm->state]);
if (mm->state == GSM48_MM_ST_MM_ILDE)
@@ -2629,7 +2667,7 @@ static int gsm48_mm_data_ind(struct osmocom_ms *ms, struct msgb *msg)
if (!conn) {
conn = mm_conn_new(mm, pdisc, trans_id,
mm_conn_new_ref++);
- msg_type = GSM48_MMCC_EST_IND;
+ rr_prim = rr_est;
}
if (!conn)
return -ENOMEM;
@@ -2637,7 +2675,7 @@ static int gsm48_mm_data_ind(struct osmocom_ms *ms, struct msgb *msg)
/* push new header */
msgb_push(msg, sizeof(struct gsm48_mmxx_hdr));
mmh = (struct gsm48_mmxx_hdr *)msg->data;
- mmh->msg_type = msg_type;
+ mmh->msg_type = rr_prim;
mmh->ref = conn->ref;
/* go MM CONN ACTIVE state */
diff --git a/src/host/gsm48-andreas/issues.txt b/src/host/gsm48-andreas/issues.txt
index 503ea03e..19793d45 100644
--- a/src/host/gsm48-andreas/issues.txt
+++ b/src/host/gsm48-andreas/issues.txt
@@ -6,6 +6,13 @@ exists?
OpenBSC:
-if tx_setup fails during process, the msg must be freed to avoid memory leak.
+If tx_setup fails during process, the msg must be freed to avoid memory leak.
+OpenBSC:
+Must use LOC_PRN_S_LU on protocol side.
+Or it uses LOC PUN_S_LU to hide private network type (maybe some cfg option)
+
+LCR:
+Also LCR must use correct location.
+For MS support, it must use LOC_USER
diff --git a/src/host/gsm48-andreas/transaction.h b/src/host/gsm48-andreas/transaction.h
index a2f506be..efbbbe87 100644
--- a/src/host/gsm48-andreas/transaction.h
+++ b/src/host/gsm48-andreas/transaction.h
@@ -29,6 +29,9 @@ struct gsm_trans {
/* current call state */
int state;
+ /* most recent progress indicator */
+ u_int8_t prog_ind;
+
/* current timer and message queue */
int Tcurrent; /* current CC timer */
int T308_second; /* used to send release again */
@@ -52,7 +55,7 @@ struct gsm_trans {
struct gsm_trans *trans_find_by_id(struct osmocom_ms *ms,
u_int8_t proto, u_int8_t trans_id);
-struct gsm_trans *trans_find_by_callref(struct gsm_network *net,
+struct gsm_trans *trans_find_by_callref(struct osmocom_ms *ms,
u_int32_t callref);
struct gsm_trans *trans_alloc(struct osmocom_ms *ms,
diff --git a/src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h
index 801b9b54..0e2cd8e2 100644
--- a/src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h
+++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h
@@ -685,6 +685,7 @@ enum chreq_type {
/* Chapter 5.1.2.2 */
#define GSM_CSTATE_NULL 0
#define GSM_CSTATE_INITIATED 1
+#define GSM_CSTATE_MM_CONNECTION_PEND 2 /* see 10.5.4.6 */
#define GSM_CSTATE_MO_CALL_PROC 3
#define GSM_CSTATE_CALL_DELIVERED 4
#define GSM_CSTATE_CALL_PRESENT 6
diff --git a/src/shared/libosmocore/src/gsm48.c b/src/shared/libosmocore/src/gsm48.c
index 5761c67b..adaeba33 100644
--- a/src/shared/libosmocore/src/gsm48.c
+++ b/src/shared/libosmocore/src/gsm48.c
@@ -97,10 +97,10 @@ static const struct value_string rr_cause_names[] = {
};
/* FIXME: convert to value_string */
-static const char *cc_state_names[32] = {
+static const char *cc_state_names[33] = {
"NULL",
"INITIATED",
- "illegal state 2",
+ "MM_CONNECTION_PEND",
"MO_CALL_PROC",
"CALL_DELIVERED",
"illegal state 5",