summaryrefslogtreecommitdiffstats
path: root/src/host/gsm48-andreas
diff options
context:
space:
mode:
authorHarald Welte2010-03-07 22:48:52 +0100
committerHarald Welte2010-03-07 22:48:52 +0100
commit3b05942b631983fb3b17dca252a64f164f2b0510 (patch)
tree6b7301e8c3827f2c7af8efd75d11478e7e2da345 /src/host/gsm48-andreas
parentfix framenumber byte-ordering (diff)
downloadosmocom-3b05942b631983fb3b17dca252a64f164f2b0510.tar.gz
osmocom-3b05942b631983fb3b17dca252a64f164f2b0510.tar.xz
osmocom-3b05942b631983fb3b17dca252a64f164f2b0510.zip
import alpha3 code from andreas eversberg as of 2010-03-07
Diffstat (limited to 'src/host/gsm48-andreas')
-rw-r--r--src/host/gsm48-andreas/gsm48_cc.c1368
-rw-r--r--src/host/gsm48-andreas/gsm48_ie.c624
-rw-r--r--src/host/gsm48-andreas/gsm48_l2.h80
-rw-r--r--src/host/gsm48-andreas/gsm48_l3.h289
-rw-r--r--src/host/gsm48-andreas/gsm48_mm.c1228
-rw-r--r--src/host/gsm48-andreas/gsm48_rr.c950
-rw-r--r--src/host/gsm48-andreas/issues.txt21
7 files changed, 4560 insertions, 0 deletions
diff --git a/src/host/gsm48-andreas/gsm48_cc.c b/src/host/gsm48-andreas/gsm48_cc.c
new file mode 100644
index 0000000..1db41a1
--- /dev/null
+++ b/src/host/gsm48-andreas/gsm48_cc.c
@@ -0,0 +1,1368 @@
+fragen:
+
+warum ist layer 2 ein socket client
+wo soll layer 3 hin
+wo soll die app (layer 4) liegen, in der lib oder im con
+transactions wie im openbsc
+lchan in einer msg?: eine 1:1 beziehung zwischen ms und lchan?:
+ms instanzliste für mehrere geräte, 'name' fuer ein jedes geraet
+messages wie bei openbsc
+ist der tlv-parser bufferoverflow-safe?: was ist mit dem letzten ie?: sollte der parser nicht error zurücklifern und dann den aufrufenden prozess beenden?:
+wieso ist classmark1 in gsm_04_08.h MSB und nicht LSB wie sonst?:
+headerkonzept für inter-layer und interface
+debug?: DMM DCC DSS DSMS DRR
+
+
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * 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.
+ *
+ */
+
+/* QUICK OVERVIEW:
+ *
+ * - support functions and data
+ * - handlers for messages to lower layer (gsm48_cc_tx_*)
+ * - state machine for MNCC messages
+ * - handlers for messages from lower layer (gsm48_cc_rx_*)
+ * - state machine for lower layer messages
+ * - timeout handler
+ */
+
+/* start various timers */
+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->cc.Tcurrent = current;
+}
+
+/* stop various timers */
+static void gsm48_stop_cc_timer(struct gsm_trans *trans)
+{
+ if (bsc_timer_pending(&trans->cc.timer)) {
+ DEBUGP(DCC, "stopping pending timer T%x\n", trans->cc.Tcurrent);
+ bsc_del_timer(&trans->cc.timer);
+ trans->cc.Tcurrent = 0;
+ }
+}
+
+/* send release indication to upper layer */
+int mncc_release_ind(struct gsm_network *net, struct gsm_trans *trans,
+ u_int32_t callref, int location, int value)
+{
+ struct gsm_mncc rel;
+
+ memset(&rel, 0, sizeof(rel));
+ rel.callref = callref;
+ mncc_set_cause(&rel, location, value);
+ return mncc_recvmsg(net, 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)
+{
+ 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->msg_type = GSM48_MT_CC_STATUS;
+
+ cause = msgb_put(msg, 3);
+ cause[0] = 2;
+im openbsc muss LOC_PRIV_LOC_U verwendet werden !!!!
+ cause[1] = GSM48_CAUSE_CS_GSM | GSM48_CAUSE_LOC_USER;
+ cause[2] = 0x80 | 30; /* response to status inquiry */
+
+ call_state = msgb_put(msg, 1);
+ call_state[0] = 0xc0 | 0x00;
+
+ return gsm48_sendmsg(msg, trans);
+}
+
+/* setup message from upper layer */
+static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh;
+ struct gsm_mncc *setup = arg;
+ int rc, trans_id;
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ /* 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->subscr->net, trans, trans->callref,
+alle PRN_S_LU nach USER !!!!
+auch beim LCR für ms-mode anpassen !!!!
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ trans->callref = 0;
+ trans_free(trans);
+ return rc;
+ }
+
+ /* Get free transaction_id */
+ trans_id = trans_assign_trans_id(trans->subscr, 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,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ trans->callref = 0;
+ trans_free(trans);
+ return rc;
+ }
+ trans->transaction_id = trans_id;
+
+ gh->msg_type = (setup->emergency) ? GSM48_MT_CC_EMERG_SETUP : GSM48_MT_CC_SETUP;
+
+ gsm48_start_cc_timer(trans, 0x303, GSM48_T303_MO);
+
+ /* bearer capability */
+ encode_bearer_cap(msg, 0, &setup->bearer_cap);
+ /* facility */
+ if (setup->fields & MNCC_F_FACILITY)
+ encode_facility(msg, 0, &setup->facility);
+ /* 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);
+ /* ss version */
+ if (setup->fields & MNCC_F_SSVERSION)
+ encode_ssversion(msg, 0, &setup->ssversion);
+ /* CLIR suppression */
+ if (setup->clir.sup)
+ encode_clir_sup(msg);
+ /* CLIR invocation */
+ if (setup->clir.inv)
+ encode_clir_inv(msg);
+ /* cc cap */
+ if (setup->fields & MNCC_F_CCCAP)
+ encode_cccap(msg, 0, &setup->cccap);
+
+ new_cc_state(trans, GSM_CSTATE_CALL_INITIATED);
+
+ return gsm48_sendmsg(msg, trans);
+}
+
+/* connect ack message from upper layer */
+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->msg_type = GSM48_MT_CC_CONNECT_ACK;
+
+ new_cc_state(trans, GSM_CSTATE_ACTIVE);
+
+ return gsm48_sendmsg(msg, trans);
+}
+
+/* call conf message from upper layer */
+static int gsm48_cc_tx_call_conf(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *confirm = arg;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ 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)
+ encode_bearer_cap(msg, 0, &confirm->bearer_cap);
+ /* cause */
+ if (confirm->fields & MNCC_F_CAUSE)
+ encode_cause(msg, 0, &confirm->cause);
+ /* cc cap */
+ if (confirm->fields & MNCC_F_CCCAP)
+ encode_cccap(msg, 0, &confirm->cccap);
+
+ return gsm48_sendmsg(msg, trans);
+}
+
+/* alerting message from upper layer */
+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->msg_type = GSM48_MT_CC_ALERTING;
+
+ /* facility */
+ if (alerting->fields & MNCC_F_FACILITY)
+ encode_facility(msg, 0, &alerting->facility);
+ /* user-user */
+ if (alerting->fields & MNCC_F_USERUSER)
+ encode_useruser(msg, 0, &alerting->useruser);
+ /* ss version */
+ if (alerting->fields & MNCC_F_SSVERSION)
+ encode_ssversion(msg, 0, &alerting->ssversion);
+
+ new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED);
+
+ return gsm48_sendmsg(msg, trans);
+}
+
+/* connect message from upper layer */
+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->msg_type = GSM48_MT_CC_CONNECT;
+
+ gsm48_stop_cc_timer(trans);
+ gsm48_start_cc_timer(trans, 0x313, GSM48_T313_MS);
+
+ /* facility */
+ if (connect->fields & MNCC_F_FACILITY)
+ encode_facility(msg, 0, &connect->facility);
+ /* user-user */
+ if (connect->fields & MNCC_F_USERUSER)
+ encode_useruser(msg, 0, &connect->useruser);
+ /* ss version */
+ if (connect->fields & MNCC_F_SSVERSION)
+ encode_ssversion(msg, 0, &connect->ssversion);
+
+ new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST);
+
+ return gsm48_sendmsg(msg, trans);
+}
+
+/* notify message from upper layer */
+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->msg_type = GSM48_MT_CC_NOTIFY;
+
+ /* notify */
+ encode_notify(msg, notify->notify);
+
+ return gsm48_sendmsg(msg, trans);
+}
+
+/* start dtmf message from upper layer */
+static int gsm48_cc_tx_start_dtmf(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->msg_type = GSM48_MT_CC_START_DTMF;
+
+ /* keypad */
+ encode_keypad(msg, dtmf->keypad);
+
+ return gsm48_sendmsg(msg, trans);
+}
+
+/* stop dtmf message from upper layer */
+static int gsm48_cc_tx_stop_dtmf(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->msg_type = GSM48_MT_CC_STOP_DTMF;
+
+ return gsm48_sendmsg(msg, trans);
+}
+
+/* hold message from upper layer */
+static int gsm48_cc_tx_hold(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->msg_type = GSM48_MT_CC_HOLD;
+
+ return gsm48_sendmsg(msg, trans);
+}
+
+/* retrieve message from upper layer */
+static int gsm48_cc_tx_hold(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->msg_type = GSM48_MT_CC_RETRIEVE;
+
+ return gsm48_sendmsg(msg, trans);
+}
+
+/* disconnect message from upper layer or from timer event */
+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->msg_type = GSM48_MT_CC_DISCONNECT;
+
+ gsm48_stop_cc_timer(trans);
+ gsm48_start_cc_timer(trans, 0x305, GSM48_T305_MS);
+
+ /* 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);
+ /* ss version */
+ if (disc->fields & MNCC_F_SSVERSION)
+ encode_ssversion(msg, 0, &disc->ssversion);
+
+ new_cc_state(trans, GSM_CSTATE_DISCONNECT_REQ);
+
+ return gsm48_sendmsg(msg, trans);
+}
+
+/* release message from upper layer or from timer event */
+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->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)
+ 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);
+ /* ss version */
+ if (rel->fields & MNCC_F_SSVERSION)
+ encode_ssversion(msg, 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 gsm48_sendmsg(msg, trans);
+}
+
+/* reject message from upper layer or from timer event */
+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->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);
+ /* ss version */
+ if (rel->fields & MNCC_F_SSVERSION)
+ encode_ssversion(msg, 0, &rel->ssversion);
+
+ trans_free(trans);
+
+ return gsm48_sendmsg(msg, trans);
+}
+
+/* facility message from upper layer or from timer event */
+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->msg_type = GSM48_MT_CC_FACILITY;
+
+ /* facility */
+ encode_facility(msg, 1, &fac->facility);
+ /* ss version */
+ if (rel->fields & MNCC_F_SSVERSION)
+ encode_ssversion(msg, 0, &rel->ssversion);
+
+ return gsm48_sendmsg(msg, trans);
+}
+
+/* user info message from upper layer or from timer event */
+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->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, trans);
+}
+
+/* modify message from upper layer or from timer event */
+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->msg_type = GSM48_MT_CC_MODIFY;
+
+ gsm48_start_cc_timer(trans, 0x323, GSM48_T323_MS);
+
+ /* bearer capability */
+ encode_bearer_cap(msg, 1, &modify->bearer_cap);
+
+ new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY);
+
+ return gsm48_sendmsg(msg, trans);
+}
+
+/* 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 *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ 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, trans);
+}
+
+/* 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 *modify = arg;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ 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, trans);
+}
+
+/* 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))
+
+
+
+/* reply status enquiry */
+static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg)
+{
+ return gsm48_cc_tx_status(trans, msg);
+}
+
+/* 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);
+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
+ /* bearer capability */
+ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
+ call_proc.fields |= MNCC_F_BEARER_CAP;
+ 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;
+ 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;
+ decode_progress(&call_proc.progress,
+ TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
+ }
+
+ new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC);
+
+ return mncc_recvmsg(trans->subscr->net, 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;
+ 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);
+ }
+ /* user-user */
+ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+ alerting.fields |= MNCC_F_USERUSER;
+ decode_useruser(&alerting.alerting,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ }
+
+ new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED);
+
+ return mncc_recvmsg(trans->subscr->net, 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;
+ 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;
+ 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;
+ 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;
+ 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);
+}
+
+/* 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;
+ 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);
+ }
+ /* progress */
+ if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
+ setup.fields |= MNCC_F_PROGRESS;
+ 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;
+ 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;
+ 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;
+ 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;
+ 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;
+ 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->subscr->net, trans, MNCC_SETUP_IND, &setup);
+
+ return 0;
+}
+
+/* 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->subscr->net, trans, MNCC_SETUP_COMPL_IND,
+ &connect_ack);
+}
+
+/* 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;
+ }
+ decode_notify(&notify.notify, gh->data);
+
+ return mncc_recvmsg(trans->subscr->net, trans, MNCC_NOTIFY_IND, &notify);
+}
+
+/* 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;
+ 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;
+ decode_useruser(&disc.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ }
+
+ return mncc_recvmsg(trans->subscr->net, trans, MNCC_PROGRESS_IND, &progress);
+}
+
+/* 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;
+ decode_keypad(&dtmf.keypad,
+ TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1);
+ }
+
+ return mncc_recvmsg(trans->subscr->net, 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;
+ }
+ decode_cause(&dtmf.cause, gh->data);
+
+ return mncc_recvmsg(trans->subscr->net, trans, MNCC_START_DTMF_REJ, &dtmf);
+}
+
+/* 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;
+
+ memset(&dtmf, 0, sizeof(struct gsm_mncc));
+ dtmf.callref = trans->callref;
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+
+ return mncc_recvmsg(trans->subscr->net, trans, MNCC_STOP_DTMF_RSP, &dtmf);
+}
+
+/* hold ack is received from lower layer */
+static int gsm48_cc_rx_hold_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 gsm_mncc hold;
+
+ memset(&hold, 0, sizeof(struct gsm_mncc));
+ hold.callref = trans->callref;
+
+ return mncc_recvmsg(trans->subscr->net, trans, MNCC_HOLD_CNF, &hold);
+}
+
+/* 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 (payload_len < 1) {
+ DEBUGP(DCC, "Short read of hold reject message error.\n");
+ return -EINVAL;
+ }
+ decode_cause(&hold.cause, gh->data);
+
+ return mncc_recvmsg(trans->subscr->net, trans, MNCC_HOLD_REJ, &hold);
+}
+
+/* retrieve ack is received from lower layer */
+static int gsm48_cc_rx_retrieve_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 retrieve;
+
+ memset(&retrieve, 0, sizeof(struct gsm_mncc));
+ retrieve.callref = trans->callref;
+
+ return mncc_recvmsg(trans->subscr->net, trans, MNCC_RETRIEVE_CNF, &retrieve);
+}
+
+/* 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 (payload_len < 1) {
+ DEBUGP(DCC, "Short read of retrieve reject message error.\n");
+ return -EINVAL;
+ }
+ decode_cause(&retrieve.cause, gh->data);
+
+ return mncc_recvmsg(trans->subscr->net, trans, MNCC_RETRIEVE_REJ, &retrieve);
+}
+
+/* disconnect is received from lower layer */
+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_IND);
+
+ 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);
+ }
+
+ /* 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);
+
+}
+
+/* release is received from lower layer */
+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);
+ }
+
+ if (trans->cc.state == GSM_CSTATE_RELEASE_REQ) {
+ /* release collision 5.4.5 */
+ rc = mncc_recvmsg(trans->subscr->net, 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);
+ }
+
+ new_cc_state(trans, GSM_CSTATE_NULL);
+
+ trans->callref = 0;
+ trans_free(trans);
+
+ return rc;
+}
+
+/* release complete is received from lower layer */
+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);
+ }
+
+ if (trans->callref) {
+ switch (trans->cc.state) {
+ case GSM_CSTATE_CALL_PRESENT:
+ rc = mncc_recvmsg(trans->subscr->net, trans,
+ MNCC_REJ_IND, &rel);
+ break;
+ case GSM_CSTATE_RELEASE_REQ:
+ rc = mncc_recvmsg(trans->subscr->net, 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_REL_IND, &rel);
+ }
+ }
+
+ 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;
+ /* facility */
+ if (payload_len < 1) {
+ DEBUGP(DCC, "Short read of facility message error.\n");
+ return -EINVAL;
+ }
+ 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_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->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, trans);
+}
+
+/* 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;
+ }
+ 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);
+}
+
+/* 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;
+ }
+ decode_bearer_cap(&modify.bearer_cap, gh->data);
+
+ new_cc_state(trans, GSM_CSTATE_ACTIVE);
+
+ return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_CNF, &modify);
+}
+
+/* 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 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 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;
+ 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;
+ decode_cause(&modify.cause,
+ TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+ }
+
+ new_cc_state(trans, GSM_CSTATE_ACTIVE);
+
+ return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_REJ, &modify);
+}
+
+/* state trasitions for call control messages (lower layer) */
+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_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), /* 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 */
+ {SBIT(GSM_CSTATE_NULL), /* 5.2.2.1 */
+ GSM48_MT_CC_SETUP, gsm48_cc_rx_setup},
+ {SBIT(GSM_CSTATE_CALL_PRESENT), /* 5.2.2.6 */
+ GSM48_MT_CC_CONNECT_ACK, gsm48_cc_rx_connect_ack},
+ /* signalling during call */
+ {SBIT(GSM_CSTATE_ACTIVE), /* 5.3.1 */
+ GSM48_MT_CC_NOTIFY, gsm48_cc_rx_notify},
+ {ALL_STATES, /* 8.4 */
+ GSM48_MT_CC_STATUS_ENQ, gsm48_cc_rx_status_enq},
+ {ALL_STATES, /* 5.5.7.2 */
+ GSM48_MT_START_DTMF_RSP, gsm48_cc_rx_start_dtmf_ack},
+ {ALL_STATES, /* 5.5.7.2 */
+ GSM48_MT_START_DTMF_REJ, gsm48_cc_rx_start_dtmf_rej},
+ {ALL_STATES, /* 5.5.7.4 */
+ GSM48_MT_STOP_DTMF_RSP, gsm48_cc_rx_stop_dtmf_ack},
+ {SBIT(GSM_CSTATE_ACTIVE),
+ GSM48_MT_HOLD_CNF, gsm48_cc_rx_hold_ack},
+ {SBIT(GSM_CSTATE_ACTIVE),
+ GSM48_MT_HOLD_REJ, gsm48_cc_rx_hold_rej},
+ {SBIT(GSM_CSTATE_ACTIVE),
+ GSM48_MT_RETRIEVE_CNF, gsm48_cc_rx_retrieve_ack},
+ {SBIT(GSM_CSTATE_ACTIVE),
+ GSM48_MT_RETRIEVE_REJ, gsm48_cc_rx_retrieve_rej},
+ {ALL_STATES - SBIT(GSM_CSTATE_NULL),
+ GSM48_MT_CC_FACILITY, gsm48_cc_rx_facility},
+ {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) - SBIT(GSM_CSTATE_DISCONNECT_IND), /* 5.4.4.1.1 */
+ GSM48_MT_CC_DISCONNECT, gsm48_cc_rx_disconnect},
+ {ALL_STATES - SBIT(GSM_CSTATE_NULL), /* 5.4.3.3 & 5.4.5!!!*/
+ GSM48_MT_CC_RELEASE, gsm48_cc_rx_release},
+ {ALL_STATES, /* 5.4.4.1.3 */
+ GSM48_MT_CC_RELEASE_COMPL, gsm48_cc_rx_release_compl},
+ /* modify */
+ {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},
+};
+
+#define DATASLLEN \
+ (sizeof(datastatelist) / sizeof(struct datastate))
+
+
+/* timeout events of all timers */
+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;
+im openbsc muss LOC_PRIV_LOC_U verwendet werden !!!!
+ 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->cc.Tcurrent) {
+ case 0x303:
+ 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 */
+ }
+ trans_free(trans);
+ return;
+ 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 && 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);
+ }
+
+}
+
diff --git a/src/host/gsm48-andreas/gsm48_ie.c b/src/host/gsm48-andreas/gsm48_ie.c
new file mode 100644
index 0000000..060dc29
--- /dev/null
+++ b/src/host/gsm48-andreas/gsm48_ie.c
@@ -0,0 +1,624 @@
+static const char bcd_num_digits[] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', '*', '#', 'a', 'b', 'c', '\0'
+};
+
+/* decode a 'called/calling/connect party BCD number' as in 10.5.4.7 */
+int decode_bcd_number(char *output, int output_len, const u_int8_t *bcd_lv,
+ int h_len)
+{
+ u_int8_t in_len = bcd_lv[0];
+ int i;
+
+ for (i = 1 + h_len; i <= in_len; i++) {
+ /* lower nibble */
+ output_len--;
+ if (output_len <= 1)
+ break;
+ *output++ = bcd_num_digits[bcd_lv[i] & 0xf];
+
+ /* higher nibble */
+ output_len--;
+ if (output_len <= 1)
+ break;
+ *output++ = bcd_num_digits[bcd_lv[i] >> 4];
+ }
+ if (output_len >= 1)
+ *output++ = '\0';
+
+ return 0;
+}
+
+/* convert a single ASCII character to call-control BCD */
+static int asc_to_bcd(const char asc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bcd_num_digits); i++) {
+ if (bcd_num_digits[i] == asc)
+ return i;
+ }
+ return -EINVAL;
+}
+
+/* convert a ASCII phone number to 'called/calling/connect party BCD number' */
+int encode_bcd_number(u_int8_t *bcd_lv, u_int8_t max_len,
+ int h_len, const char *input)
+{
+ int in_len = strlen(input);
+ int i;
+ u_int8_t *bcd_cur = bcd_lv + 1 + h_len;
+
+ /* two digits per byte, plus type byte */
+ bcd_lv[0] = in_len/2 + h_len;
+ if (in_len % 2)
+ bcd_lv[0]++;
+
+ if (bcd_lv[0] > max_len)
+ return -EIO;
+
+ for (i = 0; i < in_len; i++) {
+ int rc = asc_to_bcd(input[i]);
+ if (rc < 0)
+ return rc;
+ if (i % 2 == 0)
+ *bcd_cur = rc;
+ else
+ *bcd_cur++ |= (rc << 4);
+ }
+ /* append padding nibble in case of odd length */
+ if (i % 2)
+ *bcd_cur++ |= 0xf0;
+
+ /* return how many bytes we used */
+ return (bcd_cur - bcd_lv);
+}
+
+/* decode 'bearer capability' */
+int decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
+ const u_int8_t *lv)
+{
+ u_int8_t in_len = lv[0];
+ int i, s;
+
+ if (in_len < 1)
+ return -EINVAL;
+
+ bcap->speech_ver[0] = -1; /* end of list, of maximum 7 values */
+
+ /* octet 3 */
+ bcap->transfer = lv[1] & 0x07;
+ bcap->mode = (lv[1] & 0x08) >> 3;
+ bcap->coding = (lv[1] & 0x10) >> 4;
+ bcap->radio = (lv[1] & 0x60) >> 5;
+
+ if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) {
+ i = 1;
+ s = 0;
+ while(!(lv[i] & 0x80)) {
+ i++; /* octet 3a etc */
+ if (in_len < i)
+ return 0;
+ bcap->speech_ver[s++] = lv[i] & 0x0f;
+ bcap->speech_ver[s] = -1; /* end of list */
+ if (i == 2) /* octet 3a */
+ bcap->speech_ctm = (lv[i] & 0x20) >> 5;
+ if (s == 7) /* maximum speech versions + end of list */
+ return 0;
+ }
+ } else {
+ i = 1;
+ while (!(lv[i] & 0x80)) {
+ i++; /* octet 3a etc */
+ if (in_len < i)
+ return 0;
+ /* ignore them */
+ }
+ /* FIXME: implement OCTET 4+ parsing */
+ }
+
+ return 0;
+}
+
+/* encode 'bearer capability' */
+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 = 1, s;
+
+ lv[1] = bcap->transfer;
+ lv[1] |= bcap->mode << 3;
+ lv[1] |= bcap->coding << 4;
+ lv[1] |= bcap->radio << 5;
+
+ if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) {
+ for (s = 0; bcap->speech_ver[s] >= 0; s++) {
+ i++; /* octet 3a etc */
+ lv[i] = bcap->speech_ver[s];
+ if (i == 2) /* octet 3a */
+ lv[i] |= bcap->speech_ctm << 5;
+ }
+ lv[i] |= 0x80; /* last IE of octet 3 etc */
+ } else {
+ /* FIXME: implement OCTET 4+ encoding */
+ }
+
+ lv[0] = i;
+ if (lv_only)
+ msgb_lv_put(msg, lv[0], lv+1);
+ else
+ msgb_tlv_put(msg, GSM48_IE_BEARER_CAP, lv[0], lv+1);
+
+ return 0;
+}
+
+/* decode 'call control cap' */
+int decode_cccap(struct gsm_mncc_cccap *ccap, const u_int8_t *lv)
+{
+ u_int8_t in_len = lv[0];
+
+ if (in_len < 1)
+ return -EINVAL;
+
+ /* octet 3 */
+ ccap->dtmf = lv[1] & 0x01;
+ ccap->pcp = (lv[1] & 0x02) >> 1;
+
+ return 0;
+}
+
+/* encode 'call control cap' */
+int encode_cccap(struct msgb *msg,
+ const struct gsm_mncc_cccap *ccap)
+{
+ u_int8_t lv[2];
+ int ret;
+
+ lv[0] = 1;
+ lv[1] = 0;
+ if (ccap->dtmf)
+ lv [1] |= 0x01;
+ if (ccap->cpc)
+ lv [1] |= 0x02;
+
+ msgb_tlv_put(msg, GSM48_IE_CCCAP, lv[0], lv+1);
+
+ return 0;
+}
+
+/* decode 'called party BCD number' */
+int decode_called(struct gsm_mncc_number *called,
+ const u_int8_t *lv)
+{
+ u_int8_t in_len = lv[0];
+
+ if (in_len < 1)
+ return -EINVAL;
+
+ /* octet 3 */
+ called->plan = lv[1] & 0x0f;
+ called->type = (lv[1] & 0x70) >> 4;
+
+ /* octet 4..N */
+ decode_bcd_number(called->number, sizeof(called->number), lv, 1);
+
+ return 0;
+}
+
+/* encode 'called party BCD number' */
+int encode_called(struct msgb *msg,
+ const struct gsm_mncc_number *called)
+{
+ u_int8_t lv[18];
+ int ret;
+
+ /* octet 3 */
+ lv[1] = called->plan;
+ lv[1] |= called->type << 4;
+
+ /* octet 4..N, octet 2 */
+ ret = encode_bcd_number(lv, sizeof(lv), 1, called->number);
+ if (ret < 0)
+ return ret;
+
+ msgb_tlv_put(msg, GSM48_IE_CALLED_BCD, lv[0], lv+1);
+
+ return 0;
+}
+
+/* decode callerid of various IEs */
+int decode_callerid(struct gsm_mncc_number *callerid,
+ const u_int8_t *lv)
+{
+ u_int8_t in_len = lv[0];
+ int i = 1;
+
+ if (in_len < 1)
+ return -EINVAL;
+
+ /* octet 3 */
+ callerid->plan = lv[1] & 0x0f;
+ callerid->type = (lv[1] & 0x70) >> 4;
+
+ /* octet 3a */
+ if (!(lv[1] & 0x80)) {
+ callerid->screen = lv[2] & 0x03;
+ callerid->preent = (lv[2] & 0x60) >> 5;
+ i = 2;
+ }
+
+ /* octet 4..N */
+ decode_bcd_number(callerid->number, sizeof(callerid->number), lv, i);
+
+ return 0;
+}
+
+/* encode callerid of various IEs */
+int encode_callerid(struct msgb *msg, int ie, int max_len,
+ const struct gsm_mncc_number *callerid)
+{
+ u_int8_t lv[max_len - 1];
+ int h_len = 1;
+ int ret;
+
+ /* octet 3 */
+ lv[1] = callerid->plan;
+ lv[1] |= callerid->type << 4;
+
+ if (callerid->present || callerid->screen) {
+ /* octet 3a */
+ 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, callerid->number);
+ if (ret < 0)
+ return ret;
+
+ msgb_tlv_put(msg, ie, lv[0], lv+1);
+
+ return 0;
+}
+
+/* decode 'cause' */
+int decode_cause(struct gsm_mncc_cause *cause,
+ const u_int8_t *lv)
+{
+ u_int8_t in_len = lv[0];
+ int i;
+
+ if (in_len < 2)
+ return -EINVAL;
+
+ cause->diag_len = 0;
+
+ /* octet 3 */
+ 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;
+ cause->rec = 1;
+ cause->rec_val = lv[i] & 0x7f;
+
+ }
+ i++;
+
+ /* octet 4 */
+ cause->value = lv[i] & 0x7f;
+ i++;
+
+ if (in_len < i) /* no diag */
+ return 0;
+
+ if (in_len - (i-1) > 32) /* maximum 32 octets */
+ return 0;
+
+ /* octet 5-N */
+ memcpy(cause->diag, lv + i, in_len - (i-1));
+ cause->diag_len = in_len - (i-1);
+
+ return 0;
+}
+
+/* encode 'cause' */
+int encode_cause(struct msgb *msg, int lv_only,
+ const struct gsm_mncc_cause *cause)
+{
+ u_int8_t lv[32+4];
+ int i;
+
+ if (cause->diag_len > 32)
+ return -EINVAL;
+
+ /* octet 3 */
+ lv[1] = cause->location;
+ lv[1] |= cause->coding << 5;
+
+ i = 1;
+ if (cause->rec) {
+ i++; /* octet 3a */
+ lv[i] = cause->rec_val;
+ }
+ lv[i] |= 0x80; /* end of octet 3 */
+
+ /* octet 4 */
+ i++;
+ lv[i] = 0x80 | cause->value;
+
+ /* octet 5-N */
+ if (cause->diag_len) {
+ memcpy(lv + i, cause->diag, cause->diag_len);
+ i += cause->diag_len;
+ }
+
+ lv[0] = i;
+ if (lv_only)
+ msgb_lv_put(msg, lv[0], lv+1);
+ else
+ msgb_tlv_put(msg, GSM48_IE_CAUSE, lv[0], lv+1);
+
+ return 0;
+}
+
+/* decode 'calling number' */
+int decode_calling(struct gsm_mncc_number *calling,
+ const u_int8_t *lv)
+{
+ return decode_callerid(calling, lv);
+}
+
+/* encode 'calling number' */
+int encode_calling(struct msgb *msg,
+ const struct gsm_mncc_number *calling)
+{
+ return encode_callerid(msg, GSM48_IE_CALLING_BCD, 14, calling);
+}
+
+/* decode 'connected number' */
+int decode_connected(struct gsm_mncc_number *connected,
+ const u_int8_t *lv)
+{
+ return decode_callerid(calling, lv);
+}
+
+/* encode 'connected number' */
+int encode_connected(struct msgb *msg,
+ const struct gsm_mncc_number *connected)
+{
+ return encode_callerid(msg, GSM48_IE_CONN_BCD, 14, connected);
+}
+
+/* decode 'redirecting number' */
+int decode_redirecting(struct gsm_mncc_number *redirecting,
+ const u_int8_t *lv)
+{
+ return decode_callerid(calling, lv);
+}
+
+/* encode 'redirecting number' */
+int encode_redirecting(struct msgb *msg,
+ const struct gsm_mncc_number *redirecting)
+{
+ return encode_callerid(msg, GSM48_IE_REDIR_BCD, 19, redirecting);
+}
+
+/* decode 'facility' */
+int decode_facility(struct gsm_mncc_facility *facility,
+ const u_int8_t *lv)
+{
+ u_int8_t in_len = lv[0];
+
+ if (in_len < 1)
+ return -EINVAL;
+
+ if (in_len > sizeof(facility->info))
+ return -EINVAL;
+
+ memcpy(facility->info, lv+1, in_len);
+ facility->len = in_len;
+
+ return 0;
+}
+
+/* encode 'facility' */
+int encode_facility(struct msgb *msg, int lv_only,
+ const struct gsm_mncc_facility *facility)
+{
+ u_int8_t lv[GSM_MAX_FACILITY + 1];
+
+ if (facility->len < 1 || facility->len > GSM_MAX_FACILITY)
+ return -EINVAL;
+
+ memcpy(lv+1, facility->info, facility->len);
+ lv[0] = facility->len;
+ if (lv_only)
+ msgb_lv_put(msg, lv[0], lv+1);
+ else
+ msgb_tlv_put(msg, GSM48_IE_FACILITY, lv[0], lv+1);
+
+ return 0;
+}
+
+/* decode 'notify' */
+int decode_notify(int *notify, const u_int8_t *v)
+{
+ *notify = v[0] & 0x7f;
+
+ return 0;
+}
+
+/* encode 'notify' */
+int encode_notify(struct msgb *msg, int notify)
+{
+ msgb_v_put(msg, notify | 0x80);
+
+ return 0;
+}
+
+/* decode 'signal' */
+int decode_signal(int *signal, const u_int8_t *v)
+{
+ *signal = v[0];
+}
+
+/* encode 'signal' */
+int encode_signal(struct msgb *msg, int signal)
+{
+ msgb_tv_put(msg, GSM48_IE_SIGNAL, signal);
+
+ return 0;
+}
+
+/* decode 'keypad' */
+int decode_keypad(int *keypad, const u_int8_t *lv)
+{
+ u_int8_t in_len = lv[0];
+
+ if (in_len < 1)
+ return -EINVAL;
+
+ *keypad = lv[1] & 0x7f;
+
+ return 0;
+}
+
+/* encode 'keypad' */
+int encode_keypad(struct msgb *msg, int keypad)
+{
+ msgb_tv_put(msg, GSM48_IE_KEYPAD, keypad);
+
+ return 0;
+}
+
+/* decode 'progress' */
+int decode_progress(struct gsm_mncc_progress *progress,
+ const u_int8_t *lv)
+{
+ u_int8_t in_len = lv[0];
+
+ if (in_len < 2)
+ return -EINVAL;
+
+ progress->coding = (lv[1] & 0x60) >> 5;
+ progress->location = lv[1] & 0x0f;
+ progress->descr = lv[2] & 0x7f;
+
+ return 0;
+}
+
+/* encode 'progress' */
+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 | ((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
+ msgb_tlv_put(msg, GSM48_IE_PROGR_IND, lv[0], lv+1);
+
+ return 0;
+}
+
+/* decode 'user-user' */
+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;
+
+ uu->proto = lv[1];
+
+ for (i = 2; i <= in_len; i++) {
+ info_len--;
+ if (info_len <= 1)
+ break;
+ *info++ = lv[i];
+ }
+ if (info_len >= 1)
+ *info++ = '\0';
+
+ return 0;
+}
+
+/* encode 'useruser' */
+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(uu->info) > GSM_MAX_USERUSER)
+ return -EINVAL;
+
+ 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
+ msgb_tlv_put(msg, GSM48_IE_USER_USER, lv[0], lv+1);
+
+ return 0;
+}
+
+/* decode 'ss version' */
+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 < sizeof(ssv->info))
+ return -EINVAL;
+
+ memcpy(ssv->info, lv + 1, in_len);
+ ssv->len = in_len;
+
+ return 0;
+}
+
+/* encode 'ss version' */
+int encode_ssversion(struct msgb *msg,
+ const struct gsm_mncc_ssversion *ssv)
+{
+ u_int8_t lv[GSM_MAX_SSVERSION + 1];
+
+ if (ssv->len > GSM_MAX_SSVERSION)
+ return -EINVAL;
+
+ lv[0] = ssv->len;
+ memcpy(lv + 1, ssv->info, ssv->ssversion.len);
+ msgb_tlv_put(msg, GSM48_IE_SSVERSION, lv[0], lv+1);
+
+ return 0;
+}
+
+/* decode 'more data' does not require a function, because it has no value */
+
+/* encode 'more data' */
+int encode_more(struct msgb *msg)
+{
+ u_int8_t *ie;
+
+ ie = msgb_put(msg, 1);
+ ie[0] = GSM48_IE_MORE_DATA;
+
+ return 0;
+}
+
diff --git a/src/host/gsm48-andreas/gsm48_l2.h b/src/host/gsm48-andreas/gsm48_l2.h
new file mode 100644
index 0000000..7ae1440
--- /dev/null
+++ b/src/host/gsm48-andreas/gsm48_l2.h
@@ -0,0 +1,80 @@
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * 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.
+ *
+ */
+
+/* link layer primitives */
+
+#define DL_UNIT_DATA_REQ 0x0110
+#define DL_UNIT_DATA_IND 0x0112
+#define DL_DATA_REQ 0x0120
+#define DL_DATA_IND 0x0122
+#define DL_ESTABLISH_REQ 0x0130
+#define DL_ESTABLISH_IND 0x0132
+#define DL_ESTABLISH_CNF 0x0131
+#define DL_SUSPEND_REQ 0x0140
+#define DL_SUSPEND_CNF 0x0141
+#define DL_RESUME_REQ 0x0150
+#define DL_RESUME_CNF 0x0151
+#define DL_CONNECT_REQ 0x0160
+#define DL_CONNECT_CNF 0x0161
+#define DL_RELEASE_REQ 0x0170
+#define DL_RELEASE_IND 0x0172
+#define DL_RELEASE_CNF 0x0171
+#define DL_RECONNECT_REQ 0x0180
+#define DL_RANDOM_ACCESS_REQ 0x0190
+#define DL_RANDOM_ACCESS_IND 0x0192
+#define DL_RANDOM_ACCESS_CNF 0x0191
+#define DL_RANDOM_ACCESS_FLU 0x0195 /* special flush message */
+#define MDL_ERROR_IND 0x0212
+#define MDL_RELEASE_REQ 0x0220
+
+/* report error cause to layer 3 (GSM 04.06 4.1.3.5) */
+
+#define L2L3_CAUSE_T200_N200 1
+#define L2L3_CAUSE_REEST_REQ 2
+#define L2L3_CAUSE_UNSOL_UA_RSP 3
+#define L2L3_CAUSE_UNSOL_DM_RSP 4
+#define L2L3_CAUSE_UNSOL_DM_RSP_MF 5
+#define L2L3_CAUSE_UNSOL_SUPER_RSP 6
+#define L2L3_CAUSE_SEQ_ERROR 7
+#define L2L3_CAUSE_U_FRAME_INCORR 8
+#define L2L3_CAUSE_SHORT_L1HEAD1_NOTSUP 9
+#define L2L3_CAUSE_SHORT_L1HEAD1_NOTAPP 10
+#define L2L3_CAUSE_S_FRAME_INCORR 11
+#define L2L3_CAUSE_I_FRAME_INCORR_M 12
+#define L2L3_CAUSE_I_FRAME_INCORR_LEN 13
+#define L2L3_CAUSE_FRAME_NOT_IMPL 14
+#define L2L3_CAUSE_SABM_MULTI 15
+#define L2L3_CAUSE_SABM_INFO 16
+
+/* DL_* messages */
+struct gsm_dl {
+ int msg_type;
+ struct msgb msg;
+ u_int8_t cause;
+ int delay;
+ u_int8_t channel_request[3];
+ u_int8_t mobile_alloc_lv[9];
+};
+
+
+
+
+
diff --git a/src/host/gsm48-andreas/gsm48_l3.h b/src/host/gsm48-andreas/gsm48_l3.h
new file mode 100644
index 0000000..624688c
--- /dev/null
+++ b/src/host/gsm48-andreas/gsm48_l3.h
@@ -0,0 +1,289 @@
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * 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.
+ *
+ */
+
+/* application layer primitives */
+
+/* GSM 6.1.2 */
+#define GSM_MMR_REG_REQ 0x1101
+#define GSM_MMR_REG_CNF 0x1102
+#define GSM_MMR_NREG_REQ 0x1103
+#define GSM_MMR_NREG_IND 0x1104
+
+#define MMR_F_IMSI 0x0001
+#define MMR_F_CAUSE 0x0002
+
+struct gsm_mmr {
+ u_int32_t msg_type;
+
+ u_int32_t fields;
+ char imsi[16];
+ u_int8_t cause;
+};
+
+/* GSM 6.3.2 */
+#define GSM_MNSS_BEGIN_REQ 0x2110
+#define GSM_MNSS_BEGIN_IND 0x2112
+#define GSM_MNSS_FACILITY_REQ 0x2120
+#define GSM_MNSS_FACILITY_IND 0x2122
+#define GSM_MNSS_END_REQ 0x2130
+#define GSM_MNSS_END_IND 0x2132
+
+#define MNSS_F_REGISTER 0x0001
+#define MNSS_F_FACILITY 0x0002
+#define MNSS_F_RELCOMPL 0x0004
+
+struct gsm_mnss {
+ u_int32_t msg_type;
+ u_int32_t callref;
+
+ u_int32_t fields;
+ struct gsm_mnss_register register;
+ struct gsm_mnss_facility facility;
+ struct gsm_mnss_release_compl release_compl;
+};
+
+/* interlayer primitives */
+
+/* GSM 04.07 9.1.2 */
+#define RR_EST_REQ 0x8110
+#define RR_EST_IND 0x8112
+#define RR_EST_CNF 0x8111
+#define RR_REL_IND 0x8122
+#define RR_SYNC_IND 0x8132
+#define RR_DATA_REQ 0x8140
+#define RR_DATA_IND 0x8142
+#define RR_UNIT_DATA_IND 0x8152
+#define RR_ABORT_REQ 0x8160
+#define RR_ABORT_IND 0x8162
+#define RR_ACT_REQ 0x8170
+
+struct gsm_rr {
+ u_int32_t msg_type; /* RR_* primitive */
+ struct msgb *msg; /* gsm48 msg */
+ u_int8_t cause;
+};
+
+/* GSM 04.07 9.2.2 */
+#define MMCC_EST_REQ 0x9110
+#define MMCC_EST_IND 0x9112
+#define MMCC_EST_CNF 0x9111
+#define MMCC_REL_REQ 0x9120
+#define MMCC_REL_IND 0x9122
+#define MMCC_DATA_REQ 0x9130
+#define MMCC_DATA_IND 0x9132
+#define MMCC_UNIT_DATA_REQ 0x9140
+#define MMCC_UNIT_DATA_IND 0x9142
+#define MMCC_SYNC_IND 0x9152
+#define MMCC_REEST_REQ 0x9160
+#define MMCC_REEST_CNF 0x9161
+#define MMCC_ERR_IND 0x9172
+#define MMCC_PROMPT_IND 0x9182
+#define MMCC_PROMPT_REJ 0x9184
+#define MMSS_EST_REQ 0x9210
+#define MMSS_EST_IND 0x9212
+#define MMSS_EST_CNF 0x9211
+#define MMSS_REL_REQ 0x9220
+#define MMSS_REL_IND 0x9222
+#define MMSS_DATA_REQ 0x9230
+#define MMSS_DATA_IND 0x9232
+#define MMSS_UNIT_DATA_REQ 0x9240
+#define MMSS_UNIT_DATA_IND 0x9242
+#define MMSS_REEST_REQ 0x9260
+#define MMSS_REEST_CNF 0x9261
+#define MMSS_ERR_IND 0x9272
+#define MMSS_PROMPT_IND 0x9282
+#define MMSS_PROMPT_REJ 0x9284
+#define MMSMS_EST_REQ 0x9310
+#define MMSMS_EST_IND 0x9312
+#define MMSMS_EST_CNF 0x9311
+#define MMSMS_REL_REQ 0x9320
+#define MMSMS_REL_IND 0x9322
+#define MMSMS_DATA_REQ 0x9330
+#define MMSMS_DATA_IND 0x9332
+#define MMSMS_UNIT_DATA_REQ 0x9340
+#define MMSMS_UNIT_DATA_IND 0x9342
+#define MMSMS_REEST_REQ 0x9360
+#define MMSMS_REEST_CNF 0x9361
+#define MMSMS_ERR_IND 0x9372
+#define MMSMS_PROMPT_IND 0x9382
+#define MMSMS_PROMPT_REJ 0x9384
+
+/* GSM 04.07 9.1.1 */
+#define GSM_RRSTATE_IDLE 0
+#define GSM_RRSTATE_CONN_PEND 1
+#define GSM_RRSTATE_DEDICATED 2
+
+/* GSM 04.07 6.1.1 */
+#define GSM_MMRSTATE_NOTUPDATED 0
+#define GSM_MMRSTATE_WAIT 1
+#define GSM_MMRSTATE_UPDATED 2
+
+/* GSM 04.07 9.2.1 */
+#define GSM_MMCCSTATE_IDLE 0
+#define GSM_MMCCSTATE_CONN_PEND 1
+#define GSM_MMCCSTATE_DEDICATED 2
+#define GSM_MMCCSTATE_CONN_SUSP 3
+#define GSM_MMCCSTATE_REESTPEND 4
+#define GSM_MMSSSTATE_IDLE 0
+#define GSM_MMSSSTATE_CONN_PEND 1
+#define GSM_MMSSSTATE_DEDICATED 2
+#define GSM_MMSSSTATE_CONN_SUSP 3
+#define GSM_MMSSSTATE_REESTPEND 4
+#define GSM_MMSMSSTATE_IDLE 0
+#define GSM_MMSMSSTATE_CONN_PEND 1
+#define GSM_MMSMSSTATE_DEDICATED 2
+#define GSM_MMSMSSTATE_CONN_SUSP 3
+#define GSM_MMSMSSTATE_REESTPEND 4
+
+/* GSM 04.08 4.1.2.1 */
+#define GSM_MMSTATE_NULL 0
+#define GSM_MMSTATE_LOC_UPD_INIT 3
+#define GSM_MMSTATE_WAIT_OUT_MM_CONN 5
+#define GSM_MMSTATE_MM_CONN_ACTIVE 6
+#define GSM_MMSTATE_IMSI_DETACH_INIT 7
+#define GSM_MMSTATE_PROCESS_CM_SERV_P 8
+#define GSM_MMSTATE_WAIT_NETWORK_CMD 9
+#define GSM_MMSTATE_LOC_UPD_REJ 10
+#define GSM_MMSTATE_WAIT_RR_CONN_LUPD 13
+#define GSM_MMSTATE_WAIT_RR_CONN_MM_CON 14
+#define GSM_MMSTATE_WAIT_RR_CONN_IMSI_D 15
+#define GSM_MMSTATE_WAIT_REEST 17
+#define GSM_MMSTATE_WAIT_RR_ACTIVE 18
+#define GSM_MMSTATE_MM_IDLE 19
+#define GSM_MMSTATE_WAIT_ADD_OUT_MM_CON 20
+#define GSM_MMSTATE_MM_CONN_ACTIVE_VGCS 21
+#define GSM_MMSTATE_WAIT_RR_CONN_VGCS 22
+#define GSM_MMSTATE_LOC_UPD_PEND 23
+#define GSM_MMSTATE_IMSI_DETACH_PEND 24
+#define GSM_MMSTATE_RR_CONN_RELEASE_NA 25
+
+/* GSM 04.08 4.1.2.1 */
+#define GSM_MMIDLESS_NORMAL_SERVICE 1
+#define GSM_MMIDLESS_ATTEMPT_UPDATE 2
+#define GSM_MMIDLESS_LIMITED_SERVICE 3
+#define GSM_MMIDLESS_NO_IMSI 4
+#define GSM_MMIDLESS_NO_CELL_AVAIL 5
+#define GSM_MMIDLESS_LOC_UPD_NEEDED 6
+#define GSM_MMIDLESS_PLMN_SEARCH 7
+#define GSM_MMIDLESS_PLMN_SEARCH_NORMAL 8
+#define GSM_MMIDLESS_RX_VGCS_NORMAL 9
+#define GSM_MMIDLESS_RX_VGCS_LIMITED 10
+
+/* GSM 04.08 4.1.2.2 */
+#define GSM_MMUSTATE_U1_UPDATED 1
+#define GSM_MMUSTATE_U2_NOT_UPDATED 2
+#define GSM_MMUSTATE_U3_ROAMING_NA 3
+
+/* GSM 04.08 5.1.2.2 */
+#define GSM_CCSTATE_NULL 0
+#define GSM_CCSTATE_INITIATED 1
+#define GSM_CCSTATE_MO_CALL_PROC 3
+#define GSM_CCSTATE_CALL_DELIVERED 4
+#define GSM_CCSTATE_CALL_PRESENT 6
+#define GSM_CCSTATE_CALL_RECEIVED 7
+#define GSM_CCSTATE_CONNECT_REQUEST 8
+#define GSM_CCSTATE_MO_TERM_CALL_CONF 9
+#define GSM_CCSTATE_ACTIVE 10
+#define GSM_CCSTATE_DISCONNECT_REQ 12
+#define GSM_CCSTATE_DISCONNECT_IND 12
+#define GSM_CCSTATE_RELEASE_REQ 19
+#define GSM_CCSTATE_MO_ORIG_MODIFY 26
+#define GSM_CCSTATE_MO_TERM_MODIFY 27
+#define GSM_CCSTATE_CONNECT_IND 28
+
+/* MM events */
+#define MMEVENT_NEW_LAI 0xa001
+#define MMEVENT_TIMEOUT_T3211 0xa002
+#define MMEVENT_TIMEOUT_T3212 0xa003
+#define MMEVENT_TIMEOUT_T3213 0xa004
+#define MMEVENT_IMSI_DETACH 0xa005
+#define MMEVENT_IMSI_ATTACH 0xa006
+#define MMEVENT_POWER_OFF 0xa007
+#define MMEVENT_PAGING 0xa009
+#define MMEVENT_AUTH_RESPONSE 0xa00a
+
+struct gsm_mmevent {
+ u_int32_t msg_type;
+
+ u_int8_t sres[4];
+};
+
+/* GSM 04.08 MM timers */
+#define GSM_T3210_MS 20, 0
+#define GSM_T3211_MS 15, 0
+// T3212 is given by SYSTEM INFORMATION
+#define GSM_T3213_MS 4, 0
+#define GSM_T3220_MS 5, 0
+#define GSM_T3230_MS 15, 0
+#define GSM_T3240_MS 10, 0
+#define GSM_T3241_MS 300, 0
+
+
+/* MM sublayer instance */
+struct gsm_mmlayer {
+ struct osmocom_ms *ms;
+ int state;
+ int substate;
+ int update_state;
+ struct timer_list t3211;
+ struct timer_list t3212;
+ struct timer_list t3213;
+ int t3212_value;
+};
+
+/* GSM 04.08 RR timers */
+#define GSM_T3126_MS 5, 0
+
+/* RR sublayer instance */
+struct gsm_rrlayer {
+ struct osmocom_ms *ms;
+ int state;
+
+ struct timer_list t3126;
+ int t3126_value;
+ int rr_est_req;
+ struct gsm_msgb *rr_est_msg;
+ u_int8_t chan_req;
+ /* cr_hist must be signed and greater 8 bit */
+ int cr_hist[3];
+ u_int8_t channel_request[3];
+ u_int8_t mobile_alloc_lv[9];
+
+};
+
+#define RR_EST_CAUSE_EMERGENCY 1
+#define RR_EST_CAUSE_REESTAB_TCH_F 2
+#define RR_EST_CAUSE_REESTAB_TCH_H 3
+#define RR_EST_CAUSE_REESTAB_2_TCH_H 4
+#define RR_EST_CAUSE_ANS_PAG_ANY 5
+#define RR_EST_CAUSE_ANS_PAG_SDCCH 6
+#define RR_EST_CAUSE_ANS_PAG_TCH_F 7
+#define RR_EST_CAUSE_ANS_PAG_TCH_ANY 8
+#define RR_EST_CAUSE_ORIG_TCHF 9
+#define RR_EST_CAUSE_LOC_UPD 12
+#define RR_EST_CAUSE_OTHER_SDCCH 13
+
+#define RR_REL_CAUSE_UNDEFINED 0
+#define RR_REL_CAUSE_NOT_AUTHORIZED 1
+#define RR_REL_CAUSE_RA_FAILURE 2
+
+
+
diff --git a/src/host/gsm48-andreas/gsm48_mm.c b/src/host/gsm48-andreas/gsm48_mm.c
new file mode 100644
index 0000000..d7fba23
--- /dev/null
+++ b/src/host/gsm48-andreas/gsm48_mm.c
@@ -0,0 +1,1228 @@
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * 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.
+ *
+ */
+
+/* state transition */
+static void new_mm_state(struct gsm_mmlayer *mm, int state, int substate)
+{
+ DEBUGP(DMM, "(ms %s) new state %s", mm->ms, mm_state_names[mm->state]);
+ if (mm->state == GSM_MMSTATE_MM_ILDE)
+ DEBUGP(DMM, " substate %s", mm_substate_names[mm->substate]);
+ DEBUGP(DMM, "-> %s", mm_state_names[state]);
+ if (state == GSM_MMSTATE_MM_ILDE)
+ DEBUGP(DMM, " substate %s", mm_substate_names[substate]);
+ DEBUGP(DMM, "\n");
+
+ mm->state = state;
+ mm->substate = substate;
+}
+
+static void new_mmu_state(struct gsm_mmlayer *mm, int state)
+{
+ DEBUGP(DMM, "(ms %s) new state %s -> %s\n", mm->ms,
+ mm_ustate_names[mm->ustate], mm_ustate_names[state]);
+
+ mm->ustate = state;
+}
+
+/* 4.2.3 when returning to MM IDLE state, this function is called */
+static int gsm48_mm_return_idle(struct osmocom_ms *ms)
+{
+ /* no sim present */
+ if (no sim) {
+ /* imsi detach due to power off */
+ if (ms->mm->power_off) {
+ struct gsm_mm_msg *off;
+
+ memset(&off, 0, sizeof(off));
+ return mm_recvmsg(ms, NULL, MMEVENT_POWER_OFF, off);
+ }
+ new_mm_state(mm, MM_IDLE, GSM_MMIDLESS_NO_IMSI);
+ return 0;
+ }
+
+ /* no cell found */
+ if (no cell found) {
+ new_mm_state(mm, MM_IDLE, GSM_MMIDLESS_PLMN_SEARCH);
+ return 0;
+ }
+
+ /* return from location update with roaming not allowed */
+ if (mm->state == GSM_MMSTATE_LOC_UPD_REJ) {
+ todo 4.4.4.9 (read 4.4.4.7)
+ if (mm->lupd_rej_cause == 13) { /* roaming not allowed */
+ new_mm_state(mm, MM_IDLE, GSM_MMIDLESS_PLMN_SEARCH);
+ return 0;
+ }
+ }
+
+ /* selected cell equals the registered LAI */
+ if (selected cell->lai == ms->registered_lai) {
+ todo 4.4.4.9
+ new_mm_state(mm, MM_IDLE, GSM_MMIDLESS_NORMAL_SERVICE);
+ return 0;
+ } else {
+ /* location update not allowed */
+ if (lupd not allowed) {
+ new_mm_state(mm, MM_IDLE, GSM_MMIDLESS_LOC_UPD_NEEDED);
+ return 0;
+ } else {
+ new_mm_state(mm, MM_IDLE, GSM_MMIDLESS_LIMITED_SERVICE);
+ return 0;
+ }
+ }
+}
+
+/* TMSI reallocation complete message from upper layer */
+static int gsm48_mm_tx_tmsi_reall_cpl(struct osmocom_ms *ms, void *arg)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ msg->lchan = lchan;
+ gh->proto_discr = GSM48_PDISC_MM;
+ gh->msg_type = GSM48_MT_MM_TMSI_REALL_COMPL;
+
+ return gsm48_sendmsg(msg, NULL);
+}
+
+/* mm status message from upper layer */
+static int gsm48_mm_tx_mm_status(struct osmocom_ms *ms, void *arg)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ u_int8_t *reject_cause = msgb_put(msg, 1);
+
+ msg->lchan = lchan;
+ gh->proto_discr = GSM48_PDISC_MM;
+ gh->msg_type = GSM48_MT_MM_STATUS;
+
+ *reject_cause = *((int *)arg);
+
+ return gsm48_sendmsg(msg, NULL);
+}
+
+/* IMSI detach indication message from upper layer */
+static int gsm48_mm_tx_imsi_detach_ind(struct osmocom_ms *ms, void *arg)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ struct gsm48_classmark1 *classmark1 = msgb_put(msg, sizeof(struct gsm48_classmark1));
+ u_int8_t buf[11]; /* 1+9 should be enough, but it is not really clear */
+ u_int8_t *ie;
+
+ msg->lchan = lchan;
+ gh->proto_discr = GSM48_PDISC_MM;
+ gh->msg_type = GSM48_MT_MM_IMSI_DETACH_IND;
+
+ while(translist not empty)
+ release trans
+ todo: release trans must send a release to it's application entitity
+
+ if (att flag set) {
+ gsm48_start_mm_timer(mm, 0x3220, GSM48_T3220_MS);
+
+ new_mm_state(mm, GSM_MMSTATE_IMSI_DETACH_INIT, 0);
+
+ /* classmark 1 */
+ memcpy(classmark1, , sizeof(struct gsm48_classmark1));
+ /* MI */
+ switch(mi_type) {
+ case GSM_MI_TYPE_TMSI:
+ gsm48_generate_mid_from_tmsi(buf, tmsi);
+ break;
+ case GSM_MI_TYPE_IMSI:
+ gsm48_generate_mid_from_imsi(buf, imsi);
+ break;
+ case GSM_MI_TYPE_IMEI:
+ case GSM_MI_TYPE_IMEISV:
+ gsm48_generate_mid_from_imsi(buf, imei);
+ break;
+ case GSM_MI_TYPE_NONE:
+ default:
+ buf[0] = GSM48_IE_MOBILE_ID;
+ buf[1] = 1;
+ buf[2] = 0xf0 | GSM_MI_TYPE_NONE;
+ break;
+ }
+ /* MI as LV */
+ ie = msgb_put(msg, 1 + buf[1]);
+ memcpy(ie, buf + 1, 1 + buf[1]);
+
+ return gsm48_sendmsg(msg, NULL);
+ } else {
+ return gsm48_mm_return_idle(ms);
+ }
+
+}
+
+todo: timeout t3220
+
+static int gsm48_mm_imsi_detach_no_rr(struct osmocom_ms *ms, void *arg)
+{
+ request rr
+
+ new_mm_state(mm, GSM_MMSTATE_WAIT_RR_CONN_IMSI_D, 0);
+
+}
+
+/* identity response message from upper layer */
+static int gsm48_mm_tx_id_rsp(struct osmocom_ms *ms, u_int8_t mi_type)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ u_int8_t buf[11]; /* 1+9 should be enough, but it is not really clear */
+ u_int8_t *ie;
+
+ msg->lchan = lchan;
+ gh->proto_discr = GSM48_PDISC_MM;
+ gh->msg_type = GSM48_MT_MM_ID_RSP;
+
+ /* MI */
+ switch(mi_type) {
+ case GSM_MI_TYPE_TMSI:
+ gsm48_generate_mid_from_tmsi(buf, tmsi);
+ break;
+ case GSM_MI_TYPE_IMSI:
+ gsm48_generate_mid_from_imsi(buf, imsi);
+ break;
+ case GSM_MI_TYPE_IMEI:
+ case GSM_MI_TYPE_IMEISV:
+ gsm48_generate_mid_from_imsi(buf, imei);
+ break;
+ case GSM_MI_TYPE_NONE:
+ default:
+ buf[0] = GSM48_IE_MOBILE_ID;
+ buf[1] = 1;
+ buf[2] = 0xf0 | GSM_MI_TYPE_NONE;
+ break;
+ }
+ /* MI as LV */
+ ie = msgb_put(msg, 1 + buf[1]);
+ memcpy(ie, buf + 1, 1 + buf[1]);
+
+ return gsm48_sendmsg(msg, NULL);
+}
+
+/* cm reestablish request message from upper layer */
+static int gsm48_mm_tx_cm_serv_req(struct osmocom_ms *ms, void *arg)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ struct gsm48_service_request *serv_req = msgb_put(msg, 1 + 1 + sizeof(struct gsm48_classmark2));
+ u_int8_t *classmark2 = ((u_int8_t *)serv_req) + 1;
+ u_int8_t buf[11];
+ u_int8_t *ie;
+
+ msg->lchan = lchan;
+ gh->proto_discr = GSM48_PDISC_MM;
+ gh->msg_type = GSM48_MT_MM_CM_SERV_REQ;
+
+ /* type and key */
+ serv_req->cm_service_type =
+ serv_req->cypher_key_seq =
+ /* classmark 2 */
+ classmark2[0] = sizeof(struct gsm48_classmark2);
+ memcpy(classmark2+1, , sizeof(struct gsm48_classmark2));
+ /* MI */
+ switch(mi_type) {
+ case GSM_MI_TYPE_TMSI:
+ gsm48_generate_mid_from_tmsi(buf, tmsi);
+ break;
+ case GSM_MI_TYPE_IMSI:
+ gsm48_generate_mid_from_imsi(buf, imsi);
+ break;
+ case GSM_MI_TYPE_IMEI:
+ case GSM_MI_TYPE_IMEISV:
+ gsm48_generate_mid_from_imsi(buf, imei);
+ break;
+ case GSM_MI_TYPE_NONE:
+ default:
+ buf[0] = GSM48_IE_MOBILE_ID;
+ buf[1] = 1;
+ buf[2] = 0xf0 | GSM_MI_TYPE_NONE;
+ break;
+ }
+ /* MI as LV */
+ ie = msgb_put(msg, 1 + buf[1]);
+ memcpy(ie, buf + 1, 1 + buf[1]);
+ /* prio is optional for eMLPP */
+
+ return gsm48_sendmsg(msg, NULL);
+}
+
+/* cm service abort message from upper layer */
+static int gsm48_mm_tx_cm_service_abort(struct osmocom_ms *ms, void *arg)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ msg->lchan = lchan;
+ gh->proto_discr = GSM48_PDISC_MM;
+ gh->msg_type = GSM48_MT_MM_CM_SERV_ABORT;
+
+ return gsm48_sendmsg(msg, NULL);
+}
+
+/* cm reestablish request message from upper layer */
+static int gsm48_mm_tx_cm_reest_req(struct osmocom_ms *ms, void *arg)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ u_int8_t *key_seq = msgb_put(msg, 1);
+ u_int8_t *classmark2 = msgb_put(msg, 1 + sizeof(struct gsm48_classmark2));
+ u_int8_t buf[11];
+ u_int8_t *ie;
+
+ msg->lchan = lchan;
+ gh->proto_discr = GSM48_PDISC_MM;
+ gh->msg_type = GSM48_MT_MM_CM_REEST_REQ;
+
+ /* key */
+ *key_seq =
+ /* classmark2 */
+ classmark2[0] = sizeof(struct gsm48_classmark2);
+ memcpy(classmark2+1, , sizeof(struct gsm48_classmark2));
+ /* MI */
+ switch(mi_type) {
+ case GSM_MI_TYPE_TMSI:
+ gsm48_generate_mid_from_tmsi(buf, tmsi);
+ break;
+ case GSM_MI_TYPE_IMSI:
+ gsm48_generate_mid_from_imsi(buf, imsi);
+ break;
+ case GSM_MI_TYPE_IMEI:
+ case GSM_MI_TYPE_IMEISV:
+ gsm48_generate_mid_from_imsi(buf, imei);
+ break;
+ case GSM_MI_TYPE_NONE:
+ default:
+ buf[0] = GSM48_IE_MOBILE_ID;
+ buf[1] = 1;
+ buf[2] = 0xf0 | GSM_MI_TYPE_NONE;
+ break;
+ }
+ /* MI as LV */
+ ie = msgb_put(msg, 1 + buf[1]);
+ memcpy(ie, buf + 1, 1 + buf[1]);
+ /* LAI */
+ if (mi_type == GSM_MI_TYPE_TMSI) {
+ buf[0] = GSM48_IE_LOC_AREA;
+ gsm0408_generate_lai((struct gsm48_loc_area_id *)(buf + 1),
+ country_code, network_code, location_area_code);
+ /* LAI as TV */
+ ie = msgb_put(msg, 1 + sizeof(struct gsm48_loc_area_id));
+ memcpy(ie, buf, 1 + sizeof(struct gsm48_loc_area_id));
+ }
+
+ return gsm48_sendmsg(msg, NULL);
+}
+
+/* authentication response message from upper layer */
+static int gsm48_mm_tx_auth_rsp(struct osmocom_ms *ms, void *arg)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ struct gsm_mmevent *mmevent = arg;
+ u_int8_t *sres = msgb_put(msg, 4);
+
+ msg->lchan = lchan;
+ gh->proto_discr = GSM48_PDISC_MM;
+ gh->msg_type = GSM48_MT_MM_AUTH_RSP;
+
+ /* SRES */
+ memcpy(sres, mmevent->sres, 4);
+
+ return gsm48_sendmsg(msg, NULL);
+}
+
+/* initiate a location update */
+static int gsm48_mm_loc_update_no_rr(struct osmocom_ms *ms, void *arg)
+{
+ struct gsm_rr est;
+
+ /* stop all timers 4.4.4.1 */
+ gsm48_stop_cc_timer(mm, 0x3210);
+ gsm48_stop_cc_timer(mm, 0x3211);
+ gsm48_stop_cc_timer(mm, 0x3212);
+ gsm48_stop_cc_timer(mm, 0x3213);
+
+ memset(est, 0, sizeof(struct gsm_rr));
+todo: set cause
+ gsm48_sendrr(sm, est, RR_EST_REQ);
+
+ new_mm_state(ms, GSM_MMSTATE_WAIT_RR_CONN_LUPD, 0);
+}
+
+/* initiate an IMSI detach */
+static int gsm48_mm_imsi_detach(struct osmocom_ms *ms, void *arg)
+{
+ todo
+}
+
+/* initiate an mm connection 4.5.1.1 */
+static int gsm48_mm_init_mm_no_rr(struct osmocom_ms *ms, void *arg)
+{
+ int emergency = 0, cause;
+ struct gsm_mm *mmmsg;
+ struct gsm_trans *trans = mmmsg->trans;
+ struct gsm_rr est;
+
+todo
+
+ if (msg_type == MMCC_EST_REQ && mmmsg->emergency)
+ emergency = 1;
+
+ if (!emergency && mm->mmustate != MMUSTATE_U1_UPDATED) {
+ todo set cause
+ reject:
+ switch(msg_type) {
+ case MMCC_EST_REQ:
+ mmmsg->cause = cause;
+ return mm_recvmsg(ms, trans, MMCC_REL_IND, mmmsg);
+ case MMSS_EST_REQ:
+ mmmsg->cause = cause;
+ return mm_recvmsg(ms, trans, MMSS_REL_IND, mmmsg);
+ case MMSMS_EST_REQ:
+ mmmsg->cause = cause;
+ return mm_recvmsg(ms, trans, MMSMS_REL_IND, mmmsg);
+ default:
+ return 0;
+ }
+ }
+
+
+ switch (mm->substate) {
+ case GSM_MMIDLESS_NORMAL_SERVICE:
+ case GSM_MMIDLESS_PLMN_SEARCH_NORMAL:
+ break; /* allow when normal */
+ case GSM_MMIDLESS_ATTEMPT_UPDATE:
+ /* store mm request if attempting to update */
+ if (!emergency) {
+ store mm connection request (status waiting)
+ return trigger location update
+ }
+ break;
+ }
+ default:
+ /* reject if not emergency */
+ if (!emergency) {
+ todo set cause
+ goto reject;
+ }
+ break;
+ }
+
+ memset(est, 0, sizeof(struct gsm_rr));
+ est->cause = todo establishment cause;
+ todo: add msgb with cm_service request. todo this, change the cm_serv_req function into a general msgb-generation message for this and other functions (like service request during mm connection)
+todo: set cause
+ gsm48_sendrr(sm, est, RR_EST_REQ);
+ new_mm_state(ms, GSM_MMSTATE_WAIT_RR_CONN_MM_CON, 0);
+ return 0;
+}
+
+
+static int gsm48_mm_init_mm_first(struct osmocom_ms *ms, void *arg)
+{
+ int emergency = 0, cause;
+ struct gsm_mm *mmmsg;
+ struct gsm_trans *trans = mmmsg->trans;
+ struct gsm_rr est;
+
+ send cm service request
+
+ gsm48_stop_cc_timer(mm, 0x3241);
+ gsm48_start_mm_timer(mm, 0x3230, GSM48_T3220_MS);
+ new_mm_state(ms, GSM_MMSTATE_WAIT_OUT_MM_CONN, 0);
+}
+
+static int gsm48_mm_init_mm_more(struct osmocom_ms *ms, void *arg)
+{
+ int emergency = 0, cause;
+ struct gsm_mm *mmmsg;
+ struct gsm_trans *trans = mmmsg->trans;
+ struct gsm_rr est;
+
+ send cm service request
+
+ gsm48_stop_cc_timer(mm, 0x3241);
+ gsm48_start_mm_timer(mm, 0x3230, GSM48_T3220_MS);
+ new_mm_state(ms, GSM_MMSTATE_WAIT_ADD_OUT_MM_CONN, 0);
+}
+
+/* initiate an mm connection other cases */
+static int gsm48_mm_init_mm_other(struct osmocom_ms *ms, void *arg)
+{
+ int emergency = 0;
+ struct gsm_mm *mmmsg;
+ struct gsm_trans *trans = mmmsg->trans;
+
+ switch(msg_type) {
+ case MMCC_EST_REQ:
+ mmmsg->cause = cause;
+ return mm_recvmsg(ms, trans, MMCC_REL_IND, mmmsg);
+ case MMSS_EST_REQ:
+ mmmsg->cause = cause;
+ return mm_recvmsg(ms, trans, MMSS_REL_IND, mmmsg);
+ case MMSMS_EST_REQ:
+ mmmsg->cause = cause;
+ return mm_recvmsg(ms, trans, MMSMS_REL_IND, mmmsg);
+ default:
+ return 0;
+ }
+}
+
+/* respond to paging */
+static int gsm48_mm_paging(struct osmocom_ms *ms, void *arg)
+{
+ todo
+}
+
+/* abort RR connection */
+static int gsm48_mm_paging(struct osmocom_ms *ms, void *arg)
+{
+ struct gsm_rr abort;
+
+ memset(abort, 0, sizeof(struct gsm_rr));
+ gsm48_sendrr(sm, abort, RR_ABORT_REQ);
+}
+
+/* state trasitions for mobile managemnt messages (upper layer / events) */
+static struct eventstate {
+ u_int32_t states;
+ u_int32_t substates;
+ int type;
+ int (*rout) (struct gsm_trans *trans, void *arg);
+} eventstatelist[] = {
+ /* 4.2.2.1 */
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_NORMAL_SERVICE),
+ MMEVENT_NEW_LAI, gsm48_mm_loc_update_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_NORMAL_SERVICE),
+ MMEVENT_TIMEOUT_T3211, gsm48_mm_loc_update_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_NORMAL_SERVICE),
+ MMEVENT_TIMEOUT_T3213, gsm48_mm_loc_update_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_NORMAL_SERVICE),
+ MMEVENT_TIMEOUT_T3212, gsm48_mm_loc_update_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_NORMAL_SERVICE),
+ MMEVENT_IMSI_DETACH, gsm48_mm_imsi_detach},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_NORMAL_SERVICE),
+ MMCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_NORMAL_SERVICE),
+ MMSS_EST_REQ, gsm48_mm_init_mm_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_NORMAL_SERVICE),
+ MMSMS_EST_REQ, gsm48_mm_init_mm_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_NORMAL_SERVICE),
+ MMEVENT_PAGING, gsm48_mm_paging},
+ /* 4.2.2.2 */
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_ATTEMPT_UPDATE),
+ MMEVENT_TIMEOUT_T3211, gsm48_mm_loc_update_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_ATTEMPT_UPDATE),
+ MMEVENT_TIMEOUT_T3213, gsm48_mm_loc_update_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_ATTEMPT_UPDATE),
+ MMEVENT_NEW_LAI, gsm48_mm_loc_update_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_ATTEMPT_UPDATE),
+ MMEVENT_TIMEOUT_T3212, gsm48_mm_loc_update_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_ATTEMPT_UPDATE),
+ MMCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_ATTEMPT_UPDATE),
+ MMEVENT_PAGING, gsm48_mm_paging},
+ /* 4.2.2.3 */
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_LIMITED_SERVICE),
+ MMEVENT_NEW_LAI, gsm48_mm_loc_update_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_LIMITED_SERVICE),
+ MMCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_LIMITED_SERVICE),
+ MMEVENT_PAGING, gsm48_mm_paging},
+ /* 4.2.2.4 */
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_NO_IMSI),
+ MMCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+ /* 4.2.2.5 */
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_PLMN_SEARCH_NORMAL),
+ MMEVENT_TIMEOUT_T3211, gsm48_mm_loc_update_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_PLMN_SEARCH_NORMAL),
+ MMEVENT_TIMEOUT_T3213, gsm48_mm_loc_update_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_PLMN_SEARCH_NORMAL),
+ MMEVENT_TIMEOUT_T3212, gsm48_mm_loc_update_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_PLMN_SEARCH_NORMAL),
+ MMEVENT_IMSI_DETACH, gsm48_mm_imsi_detach},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_PLMN_SEARCH_NORMAL),
+ MMCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_PLMN_SEARCH_NORMAL),
+ MMSS_EST_REQ, gsm48_mm_init_mm_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_PLMN_SEARCH_NORMAL),
+ MMSMS_EST_REQ, gsm48_mm_init_mm_no_rr},
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_PLMN_SEARCH_NORMAL),
+ MMEVENT_PAGING, gsm48_mm_paging},
+ /* 4.2.2.4 */
+ {SBIT(GSM_MMSTATE_MM_IDLE), SBIT(GSM_MMILDESS_PLMN_SEARCH),
+ MMCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+ /* 4.5.1.1 */
+ {SBIT(GSM_MMSTATE_RR_CONN_RELEASE_NA), 0,
+ MMCC_EST_REQ, gsm48_mm_init_mm_first},
+ {SBIT(GSM_MMSTATE_RR_CONN_RELEASE_NA), 0,
+ MMSS_EST_REQ, gsm48_mm_init_mm_first},
+ {SBIT(GSM_MMSTATE_RR_CONN_RELEASE_NA), 0,
+ MMSMS_EST_REQ, gsm48_mm_init_mm_first},
+ {SBIT(GSM_MMSTATE_MM_CONN_ACTIVE), 0,
+ MMCC_EST_REQ, gsm48_mm_init_mm_more},
+ {SBIT(GSM_MMSTATE_MM_CONN_ACTIVE), 0,
+ MMSS_EST_REQ, gsm48_mm_init_mm_more},
+ {SBIT(GSM_MMSTATE_MM_CONN_ACTIVE), 0,
+ MMSMS_EST_REQ, gsm48_mm_init_mm_more},
+ {ALL_STATES, 0,
+ MMCC_EST_REQ, gsm48_mm_init_mm_other},
+ {ALL_STATES, 0,
+ MMSS_EST_REQ, gsm48_mm_init_mm_other},
+ {ALL_STATES, 0,
+ MMSMS_EST_REQ, gsm48_mm_init_mm_other},
+ /* MS events */
+ {ALL_STATES, ALL_STATES, /* 4.3.2.2 */
+ MMEVENT_AUTH_RESPONSE, gsm48_mm_tx_auth_rsp},
+ {SBIT(GSM_MMSTATE_LOC_UPD_INIT) | /* all states where RR active */
+ SBIT(GSM_MMSTATE_WAIT_OUT_MM_CONN) |
+ SBIT(GSM_MMSTATE_MM_CONN_ACTIVE) |
+ SBIT(GSM_MMSTATE_IMSI_DETACH_INIT) |
+ SBIT(GSM_MMSTATE_PROCESS_CM_SERV_P) |
+ SBIT(GSM_MMSTATE_WAIT_NETWORK_CMD) |
+ SBIT(GSM_MMSTATE_LOC_UPD_REJ) |
+ SBIT(GSM_MMSTATE_WAIT_REEST) |
+ SBIT(GSM_MMSTATE_WAIT_ADD_OUT_MM_CONN) |
+ SBIT(GSM_MMSTATE_MM_CONN_ACTIVE_VGCS) |
+ SBIT(GSM_MMSTATE_RR_CONN_RELEASE_NA), ALL_STATES, /* 4.3.4.1 */
+ MMEVENT_IMSI_DETACH, gsm48_mm_tx_imsi_detach_ind},
+ {ALL_STATES, ALL_STATES,
+ MMEVENT_IMSI_DETACH, gsm48_mm_imsi_detach_no_rr},
+todo all other states (sim removed)
+
+ {SBIT(GSM_MMSTATE_MM_IDLE), ALL_STATES,
+ MMEVENT_, gsm48_mm_tx},
+
+ /* 4.4.4.8 */
+ {SBIT(GSM_MMSTATE_WAIT_NETWORK_CMD) | SBIT(GSM_MMSTATE_LOC_UPD_REJ), ALL_STATES,
+ MMEVENT_TIMEOUT_T3240, gsm48_mm_abort_rr},
+
+ {SBIT(GSM_MMSTATE_), ALL_STATES,
+ MMEVENT_, gsm48_mm_tx},
+ {SBIT(GSM_MMSTATE_), ALL_STATES,
+ MMEVENT_, gsm48_mm_tx},
+ {SBIT(GSM_MMSTATE_), ALL_STATES,
+ MMEVENT_, gsm48_mm_tx},
+ {SBIT(GSM_MMSTATE_), ALL_STATES,
+ MMEVENT_, gsm48_mm_tx},
+ {SBIT(GSM_MMSTATE_), ALL_STATES,
+ MMEVENT_, gsm48_mm_tx},
+ {SBIT(GSM_MMSTATE_), ALL_STATES,
+ MMEVENT_, gsm48_mm_tx},
+ {SBIT(GSM_MMSTATE_), ALL_STATES,
+ MMEVENT_, gsm48_mm_tx},
+ {SBIT(GSM_MMSTATE_), ALL_STATES,
+ MMEVENT_, gsm48_mm_tx},
+};
+
+#define EVENTSLLEN \
+ (sizeof(eventstatelist) / sizeof(struct eventstate))
+
+/* MM event handler */
+int mm_event(struct osmocom_ms *ms, int msg_type, void *arg)
+{
+ struct gsm_mmlayer *mm = &ms->mmlayer;
+
+ DEBUGP(DMM, "(ms %s) Received '%s' event in state %s", ms->name,
+ get_mmevent_name(msg_type), mm_state_names[mm->state]);
+ if (mm->state == GSM_MMSTATE_MM_ILDE)
+ DEBUGP(DMM, " substate %s", mm_substate_names[mm->substate]);
+ DEBUGP(DMM, "\n");
+
+ /* Find function for current state and message */
+ for (i = 0; i < EVENTSLLEN; i++)
+ if ((msg_type == eventstatelist[i].type)
+ && ((1 << mm->state) & eventstatelist[i].states)
+ && ((1 << mm->substate) & eventstatelist[i].substates))
+ break;
+ if (i == EVENTSLLEN) {
+ DEBUGP(DMM, "Message unhandled at this state.\n");
+ return 0;
+ }
+
+ rc = eventstatelist[i].rout(ms, msg_type, arg);
+
+ return rc;
+}
+
+/* decode network name */
+static int decode_network_name(char *name, int name_len,
+ const u_int8_t *lv)
+{
+ u_int8_t in_len = lv[0];
+ int length, padding;
+
+ name[0] = '\0';
+ if (in_len < 1)
+ return -EINVAL;
+
+ /* must be CB encoded */
+ if ((lv[1] & 0x70) != 0x00)
+ return -ENOTSUP;
+
+ padding = lv[1] & 0x03;
+ length = ((in_len - 1) * 8 - padding) / 7;
+ if (length <= 0)
+ return 0;
+ if (length >= name_len)
+ length = name_len - 1;
+ gsm_7bit_decode(name, lv + 2, length);
+ name[length] = '\0';
+
+ return length;
+}
+
+static char bcd2char(u_int8_t bcd)
+{
+ if (bcd < 0xa)
+ return '0' + bcd;
+ else
+ return 'A' + (bcd - 0xa);
+}
+
+static int decode_lai(struct gsm48_loc_area_id *lai, u_int16_t *mcc, u_int16_t *mnc, u_int16_t *lac)
+{
+ *mcc = bcd2char(lai->digits[0] & 0x0f) * 100
+ + bcd2char(lai->digits[0] >> 4) * 10
+ + bcd2char(lai->digits[1] & 0x0f);
+ *mnc = bcd2char(lai->digits[1] >> 4) * 100
+ + bcd2char(lai->digits[2] & 0x0f) * 10;
+ + bcd2char(lai->digits[2] >> 4);
+ *lac = ntohs(lai->lac);
+}
+
+/* TMSI reallocation command accept is received from lower layer */
+static int gsm48_mm_rx_tmsi_realloc_cmd(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct gsm48_loc_area_id *lai = gh->data;
+ u_int8_t mi_type;
+ char *str_cur = string;
+ u_int32_t tmsi;
+
+ if (payload_len < sizeof(struct gsm48_loc_area_id) + 2) {
+ short:
+ DEBUGP(DMM, "Short read of TMSI reallocation command accept message error.\n");
+ return -EINVAL;
+ }
+ /* LAI */
+ decode_lai(lai, &ms->current_mcc, &ms->current->mnc, &ms->current_lac);
+ todo: store in sim
+ remove from forbidden list
+ /* MI */
+ mi = gh->data + sizeof(struct gsm48_loc_area_id);
+ mi_type = mi[1] & GSM_MI_TYPE_MASK;
+ switch (mi_type) {
+ case GSM_MI_TYPE_TMSI:
+ if (gh->data + sizeof(struct gsm48_loc_area_id) < 6
+ || mi[0] < 5)
+ goto short;
+ memcpy(&tmsi, mi+2, 4);
+ ms->tmsi = ntohl(tmsi);
+ ms->tmsi_valid = 1;
+ todo: store in sim
+ gsm48_mm_tx_tmsi_reall_cpl(ms);
+ break;
+ case GSM_MI_TYPE_IMSI:
+ ms->tmsi_valid = 0;
+ todo: remove tmsi from sim
+ gsm48_mm_tx_tmsi_reall_cpl(ms);
+ break;
+ default:
+ DEBUGP(DMM, "TMSI reallocation with unknown MI type %d.\n", mi_type);
+ }
+
+ return 0;
+}
+
+/* mm info is received from lower layer */
+static int gsm48_mm_rx_info(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+ /* long name */
+ if (TLVP_PRESENT(&tp, GSM48_IE_NAME_LONG)) {
+ decode_network_name(name_long, sizeof(name_long),
+ TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+ }
+ /* short name */
+ if (TLVP_PRESENT(&tp, GSM48_IE_NAME_SHORT)) {
+ decode_network_name(name_short, sizeof(name_short),
+ TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+ }
+}
+
+/* location updating reject is received from lower layer */
+static int gsm48_mm_rx_abort(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+
+ if (payload_len < 1) {
+ DEBUGP(DMM, "Short read of location updating reject message error.\n");
+ return -EINVAL;
+ }
+
+ reject_cause = *gh->data;
+
+ if (llist_empty(ms->trans)) {
+ DEBUGP(DMM, "Abort (cause #%d) while no MM connection is established.\n", reject_cause);
+ return 0;
+ } else {
+ DEBUGP(DMM, "Abort (cause #%d) while MM connection is established.\n", reject_cause);
+ while(translist not empty)
+ release trans
+ todo: release trans must send a release to it's application entitity
+ todo: release must cause release of state 6 and transfer to state 9
+ }
+
+ if (reject_cause == 6) {
+ sim: delete tmsi
+ sim: delete lai
+ sim: delete key seq number
+ nnnew_mmu_state(ms, GSM_MMUSTATE_U3_ROAMING_NA);
+ }
+}
+
+/* location updating accept is received from lower layer */
+static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ struct gsm48_loc_area_id *lai = gh->data;
+ u_int8_t *tlv_start = gh->data;
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+
+ if (payload_len < sizeof(struct gsm48_loc_area_id)) {a
+ short:
+ DEBUGP(DMM, "Short read of location updating accept message error.\n");
+ return -EINVAL;
+ }
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_LOC_AREA, 0);
+ /* LAI */
+ decode_lai(lai, &ms->current_mcc, &ms->current->mnc, &ms->current_lac);
+ todo: store in sim
+ remove from forbidden list
+
+ nnnew_mmu_state(ms, GSM_MMUSTATE_U1_UPDATED);
+
+ gsm48_stop_cc_timer(mm, 0x3210);
+
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data + sizeof(struct gsm48_loc_area_id),
+ payload_len - sizeof(struct gsm48_loc_area_id), 0, 0);
+ /* MI */
+ if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID)) {
+ mi = TLVP_VAL(&tp, GSM48_IE_FACILITY)-1;
+ if (mi[0] < 1)
+ goto short;
+ mi_type = mi[1] & GSM_MI_TYPE_MASK;
+ switch (mi_type) {
+ case GSM_MI_TYPE_TMSI:
+ if (gh->data + sizeof(struct gsm48_loc_area_id) < 6
+ || mi[0] < 5)
+ goto short;
+ memcpy(&tmsi, mi+2, 4);
+ ms->tmsi = ntohl(tmsi);
+ ms->tmsi_valid = 1;
+ todo: store in sim
+ gsm48_mm_tx_tmsi_reall_cpl(ms);
+ break;
+ case GSM_MI_TYPE_IMSI:
+ ms->tmsi_valid = 0;
+ todo: remove tmsi from sim
+ gsm48_mm_tx_tmsi_reall_cpl(ms);
+ break;
+ default:
+ DEBUGP(DMM, "TMSI reallocation with unknown MI type %d.\n", mi_type);
+ }
+ }
+ /* follow on proceed */
+ if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID)) {
+ DEBUGP(DMM, "follow-on proceed not supported.\n");
+ }
+
+ gsm48_start_mm_timer(mm, 0x3240, GSM48_T3240_MS);
+ new_mm_state(ms, GSM_MMSTATE_WAIT_NETWORK_CMD, 0);
+
+ return 0;
+}
+
+/* location update reject is received from lower layer */
+static int gsm48_mm_rx_loc_upd_rej(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+
+ if (payload_len < 1) {
+ DEBUGP(DMM, "Short read of location update reject message error.\n");
+ return -EINVAL;
+ }
+ /* reject cause */
+ reject_cause = *gh->data;
+
+ gsm48_stop_cc_timer(mm, 0x3210);
+
+ /* store until RR is released */
+ mm->lupd_rej_cause = reject_cause;
+
+ gsm48_start_mm_timer(mm, 0x3240, GSM48_T3240_MS);
+ new_mm_state(ms, GSM_MMSTATE_LOC_UPD_REJ, 0);
+}
+
+/* identitiy request is received from lower layer */
+static int gsm48_mm_rx_id_req(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ u_int8_t id_type;
+
+ if (payload_len < 1) {
+ DEBUGP(DMM, "Short read of identity request message error.\n");
+ return -EINVAL;
+ }
+ /* id type */
+ id_type = *gh->data;
+
+ gsm48_mm_tx_id_rsp(ms, id_type);
+}
+
+/* abort is received from lower layer */
+static int gsm48_mm_rx_abort(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+
+ if (payload_len < 1) {
+ DEBUGP(DMM, "Short read of abort message error.\n");
+ return -EINVAL;
+ }
+ /* reject cause */
+ reject_cause = *gh->data;
+}
+
+/* cm service reject is received from lower layer */
+static int gsm48_mm_rx_cm_service_rej(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+
+ if (payload_len < 1) {
+ DEBUGP(DMM, "Short read of cm service reject message error.\n");
+ return -EINVAL;
+ }
+ /* reject cause */
+ reject_cause = *gh->data;
+}
+
+/* cm service acknowledge is received from lower layer */
+static int gsm48_mm_rx_cm_service_ack(struct osmocom_ms *ms, struct msgb *msg)
+{
+ loop through all transactions. there must only be one in the "MMCONSTATE_CM_REQ", others are "MMCONSTATE_WAITING" or "MMCONSTATE_ACTIVE".
+ then send mm est confirm
+ change state to active
+
+ todo: an indication by the RR that the cyphering mode setting procedure is complete, shall be treated as cm_serv_ack!!
+
+ gsm48_stop_mm_timer(mm, 0x3230);
+ new_mm_state(ms, GSM_MMSTATE_MM_CONN_ACTIVE, 0);
+}
+
+/* authentication reject is received from lower layer */
+static int gsm48_mm_rx_auth_rej(struct osmocom_ms *ms, struct msgb *msg)
+{
+ ms->tmsi_valid = 0;
+ ms->sim_invalid = 1;
+ sim: delete tmsi
+ sim: delete lai
+ sim: delete key seq number
+ new_mmu_state(ms, GSM_MMUSTATE_U3_ROAMING_NA);
+
+ if (mm->state == GSM_MMSTATE_IMSI_DETACH_INIT) {
+ todo 4.3.4.3
+ }
+
+ return 0;
+}
+
+/* authentication reject is received from lower layer */
+static int gsm48_mm_rx_auth_req(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct gsm48_auth_req *ar = gh->data;
+
+ if (payload_len < sizeof(struct gsm48_auth_req)) {
+ DEBUGP(DMM, "Short read of authentication request message error.\n");
+ return -EINVAL;
+ }
+
+ /* key_seq and random */
+ new key to sim:
+ ar->key_seq;
+ ar->rand;
+
+ /* wait for auth response event from sim */
+ return 0;
+}
+
+/* state trasitions for mobile managemnt messages (lower layer) */
+static struct mmdatastate {
+ u_int32_t states;
+ int type;
+ int (*rout) (struct gsm_trans *trans, struct msgb *msg);
+} mmdatastatelist[] = {
+ {ALL_STATES, /* 4.3.1.2 */
+ GSM48_MT_MM_TMSI_REALL_CMD, gsm48_mm_rx_tmsi_realloc_cmd},
+ {ALL_STATES, /* 4.3.2.2 */
+ GSM48_MT_MM_AUTH_REQ, gsm48_mm_rx_auth_req},
+ {ALL_STATES, /* 4.3.2.5 */
+ GSM48_MT_MM_AUTH_REJ, gsm48_mm_rx_auth_rej},
+ {ALL_STATES, /* 4.3.3.2 */
+ GSM48_MT_MM_ID_REQ, gsm48_mm_rx_id_req},
+ {ALL_STATES, /* 4.3.5.2 */
+ GSM48_MT_MM_ABORT, gsm48_mm_rx_abort},
+ {ALL_STATES, /* 4.3.6.2 */
+ GSM48_MT_MM_INFO, gsm48_mm_rx_info},
+ {GSM_MMSTATE_LOC_UPD_INIT, /* 4.4.4.5 */
+ GSM48_MT_MM_LOC_UPD_ACCEPT, gsm48_mm_rx_loc_upd_acc},
+ {GSM_MMSTATE_LOC_UPD_INIT, /* 4.4.4.7 */
+ GSM48_MT_MM_LOC_UPD_ACCEPT, gsm48_mm_rx_loc_upd_rej},
+ {ALL_STATES, /* 4.5.1.1 */
+ GSM48_MT_MM_, gsm48_mm_rx_cm_service_ack},
+ {ALL_STATES,
+ GSM48_MT_MM_, gsm48_mm_rx_},
+ {ALL_STATES,
+ GSM48_MT_MM_, gsm48_mm_rx_},
+ {ALL_STATES,
+ GSM48_MT_MM_, gsm48_mm_rx_},
+
+ {SBIT(GSM_MSTATE_),
+ GSM48_MT_MM_, gsm48_mm_rx_},
+};
+
+#define DMMATASLLEN \
+ (sizeof(mmdatastatelist) / sizeof(struct mmdatastate))
+
+static int gsm48_rcv_mm(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ int msg_type = gh->msg_type & 0xbf;
+
+ DEBUGP(DMM, "(ms %s) Received '%s' from BS in state %s\n", ms->name,
+ gsm0408_mm_msg_names[msg_type], mm_state_names[mm->state]);
+
+ /* find function for current state and message */
+ for (i = 0; i < MMDATASLLEN; i++)
+ if ((msg_type == mmdatastatelist[i].type)
+ && ((1 << mm->state) & mmdatastatelist[i].states))
+ break;
+ if (i == MMDATASLLEN) {
+ DEBUGP(DMM, "Message unhandled at this state.\n");
+ return 0;
+ }
+
+ rc = mmdatastatelist[i].rout(ms, msg);
+
+ return rc;
+}
+
+/* timeout events of all timers */
+static void gsm48_mm_t3210(void *arg)
+{
+ struct gsm_mmlayer *mm = arg;
+ mm_event(mm->ms, GSM_MMEVENT_TIMEOUT_T3210);
+}
+static void gsm48_mm_t3211(void *arg)
+{
+ struct gsm_mmlayer *mm = arg;
+ mm_event(mm->ms, GSM_MMEVENT_TIMEOUT_T3211);
+}
+static void gsm48_mm_t3212(void *arg)
+{
+ struct gsm_mmlayer *mm = arg;
+ mm_event(mm->ms, GSM_MMEVENT_TIMEOUT_T3212);
+}
+static void gsm48_mm_t3213(void *arg)
+{
+ struct gsm_mmlayer *mm = arg;
+ mm_event(mm->ms, GSM_MMEVENT_TIMEOUT_T3213);
+}
+
+/* RR is esablised during location update */
+static int gsm48_mm_est_loc_upd(struct osmocom_ms *ms, struct gsm_rr *est)
+{
+ /* 4.4.4.1 */
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ struct gsm48_loc_upd *loc_upd = msgb_put(msg, 7);
+ u_int8_t *classmark1 = ((u_int8_t *)loc_upd) + 6;
+ u_int8_t buf[11];
+ u_int8_t *ie;
+
+ gsm48_start_mm_timer(mm, 0x3210, GSM48_T3210_MS);
+ new_mm_state(ms, GSM_MMSTATE_LOC_UPD_INIT, 0);
+
+ msg->lchan = lchan;
+ gh->proto_discr = GSM48_PDISC_MM;
+ gh->msg_type = GSM48_MT_MM_LOC_UPD_REQ;
+
+ /* type and key */
+ loc_upd->type =
+ loc_upd->key_seq =
+ /* classmark 1 */
+ memcpy(classmark1, , sizeof(struct gsm48_classmark1));
+ /* LAI */
+ gsm0408_generate_lai(loc_upd->lai,
+ country_code, network_code, location_area_code);
+ /* MI */
+ switch(mi_type) {
+ case GSM_MI_TYPE_TMSI:
+ gsm48_generate_mid_from_tmsi(buf, tmsi);
+ break;
+ case GSM_MI_TYPE_IMSI:
+ gsm48_generate_mid_from_imsi(buf, imsi);
+ break;
+ case GSM_MI_TYPE_IMEI:
+ case GSM_MI_TYPE_IMEISV:
+ gsm48_generate_mid_from_imsi(buf, imei);
+ break;
+ case GSM_MI_TYPE_NONE:
+ default:
+ buf[0] = GSM48_IE_MOBILE_ID;
+ buf[1] = 1;
+ buf[2] = 0xf0 | GSM_MI_TYPE_NONE;
+ break;
+ }
+ /* MI as LV */
+ ie = msgb_put(msg, 1 + buf[1]);
+ memcpy(ie, buf + 1, 1 + buf[1]);
+
+ return gsm48_sendmsg(msg, NULL);
+}
+
+/* RR is released after location update reject */
+static int gsm48_mm_rel_loc_upd_rej(struct osmocom_ms *ms, struct gsm_rr *rel)
+{
+ /* new status */
+ switch (mm->lupd_rej_cause) {
+ case 11:
+ case 12:
+ case 13:
+ attempt_counter = 0;
+ // fall through
+ case 2:
+ case 3:
+ case 6:
+ sim: delete tmsi
+ sim: delete lai
+ sim: delete key seq number
+ new_mmu_state(ms, GSM_MMUSTATE_U3_ROAMING_NA);
+ }
+
+ /* forbidden list */
+ switch (mm->lupd_rej_cause) {
+ case 2:
+ case 3:
+ case 6:
+ set sim invalid
+ break;
+ case 11:
+ add_forbidden_list(ms, FORBIDDEN_PLMN, lai);
+ break;
+ case 12:
+ add_forbidden_list(ms, FORBIDDEN_LOC_AREA_RPOS, lai);
+ break;
+ case 13:
+ add_forbidden_list(ms, FORBIDDEN_LOC_AREA_ROAM, lai);
+ break
+ default:
+ todo 4.4.4.9
+ }
+
+ return gsm48_mm_return_idle(ms);
+}
+
+/* RR is released in other states */
+static int gsm48_mm_rel_other(struct osmocom_ms *ms, struct gsm_rr *rel)
+{
+ return gsm48_mm_return_idle(ms);
+}
+
+/* RR is esablised during mm connection */
+static int gsm48_mm_est_mm_con(struct osmocom_ms *ms, struct gsm_rr *est)
+{
+ loop through all transactions. there must only be one in the "MMCONSTATE_CM_REQ", others are "MMCONSTATE_WAITING" or "MMCONSTATE_ACTIVE".
+
+ gsm48_start_mm_timer(mm, 0x3230, GSM48_T3220_MS);
+ new_mm_state(ms, GSM_MMSTATE_WAIT_OUT_MM_CONN, 0);
+}
+
+/* state trasitions for radio ressource messages (lower layer) */
+static struct rrdatastate {
+ u_int32_t states;
+ int type;
+ int (*rout) (struct gsm_trans *trans, struct gsm_rr *msg);
+} rrdatastatelist[] = {
+ {SBIT(GSM_MMSTATE_WAIT_RR_CONN_LUPD), /* 4.4.4.1 */
+ RR_ESTAB_CNF, gsm48_mm_est_loc_upd},
+ {SBIT(GSM_MMSTATE_LOC_UPD_REJ), /* 4.4.4.7 */
+ RR_REL_IND, gsm48_mm_rel_loc_upd_rej},
+ {ALL_STATES,
+ RR_REL_IND, gsm48_mm_rel_other},
+ {SBIT(GSM_MMSTATE_WAIT_RR_CONN_MM_CON), /* 4.5.1.1 */
+ RR_ESTAB_CNF, gsm48_mm_est_mm_con},
+};
+
+#define RRDATASLLEN \
+ (sizeof(rrdatastatelist) / sizeof(struct rrdatastate))
+
+static int gsm48_rcv_rr(struct osmocom_ms *ms, struct gsm_rr *rrmsg)
+{
+ int msg_type = rrmsg->msg_type;
+
+ DEBUGP(DMM, "(ms %s) Received '%s' from RR in state %s\n", ms->name,
+ gsm0408_rr_msg_names[msg_type], mm_state_names[mm->state]);
+
+ /* find function for current state and message */
+ for (i = 0; i < RRDATASLLEN; i++)
+ if ((msg_type == rrdatastatelist[i].type)
+ && ((1 << mm->state) & rrdatastatelist[i].states))
+ break;
+ if (i == RRDATASLLEN) {
+ DEBUGP(DMM, "Message unhandled at this state.\n");
+ return 0;
+ }
+
+ rc = rrdatastatelist[i].rout(ms, rrmsg);
+
+ return rc;
+}
+
+
+wichtig: nur eine MM connection zur zeit, da ja auch nur ein cm-service-request laufen kann. die anderen werden dann als "waiting" deklariert.
diff --git a/src/host/gsm48-andreas/gsm48_rr.c b/src/host/gsm48-andreas/gsm48_rr.c
new file mode 100644
index 0000000..a1dd435
--- /dev/null
+++ b/src/host/gsm48-andreas/gsm48_rr.c
@@ -0,0 +1,950 @@
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * 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.
+ *
+ */
+
+todo:
+flush rach msg in all cases: during sending, after its done, and when aborted
+stop timers on abort
+debugging. (wenn dies todo erledigt ist, bitte in den anderen code moven)
+wird beim abbruch immer der gepufferte cm-service-request entfernt, auch beim verschicken?:
+measurement reports
+todo rr_sync_ind
+
+todo change procedures, release procedure
+
+static int gsm_rr_chan2cause[4] = {
+ RR_EST_CAUSE_ANS_PAG_ANY,
+ RR_EST_CAUSE_ANS_PAG_SDCCH,
+ RR_EST_CAUSE_ANS_PAG_TCH_F,
+ RR_EST_CAUSE_ANS_PAG_TCH_ANY
+};
+
+static struct rr_names {
+ char *name;
+ int value;
+} rr_names[] = {
+ { "RR_EST_REQ", RR_EST_REQ },
+ { "RR_EST_IND", RR_EST_IND },
+ { "RR_EST_CNF", RR_EST_CNF },
+ { "RR_REL_IND", RR_REL_IND },
+ { "RR_SYNC_IND", RR_SYNC_IND },
+ { "RR_DATA_REQ", RR_DATA_REQ },
+ { "RR_DATA_IND", RR_DATA_IND },
+ { "RR_UNIT_DATA_IND", RR_UNIT_DATA_IND },
+ { "RR_ABORT_REQ", RR_ABORT_REQ },
+ { "RR_ABORT_IND", RR_ABORT_IND },
+ { "RR_ACT_REQ", RR_ACT_REQ },
+
+ {NULL, 0}
+};
+
+char *get_rr_name(int value)
+{
+ int i;
+
+ for (i = 0; rr_names[i].name; i++) {
+ if (rr_names[i].value == value)
+ return rr_names[i].name;
+ }
+
+ return "RR_Unknown";
+}
+
+static int rr_recvmsg(struct osmocom_ms *ms,
+ int msg_type, struct gsm_mncc *rrmsg)
+{
+ struct msgb *msg;
+
+ DEBUGP(DRR, "(MS %s) Sending '%s' to MM.\n", ms->name,
+ get_rr_name(msg_type));
+
+ rrmsg->msg_type = msg_type;
+
+ msg = msgb_alloc(sizeof(struct gsm_rr), "RR");
+ if (!msg)
+ return -ENOMEM;
+ memcpy(msg->data, rrmsg, sizeof(struct gsm_rr));
+ msgb_enqueue(&ms->rr.upqueue, msg);
+
+ return 0;
+}
+
+char *rr_state_names[] = {
+ "IDLE",
+ "CONN PEND",
+ "DEDICATED",
+};
+
+static void new_rr_state(struct gsm_rrlayer *rr, int state)
+{
+ if (state < 0 || state >= (sizeof(rr_state_names) / sizeof(char *)))
+ return;
+
+ if (state == GSM_RRSTATE_IDLE) {
+ rr->rr_est_req = 0;
+ if (rr->rr_est_msg) {
+ msgb_free(rr->rr_est_msg);
+ rr->rr_est_msg = NULL;
+ }
+ }
+
+ DEBUGP(DRR, "new state %s -> %s\n",
+ rr_state_names[rr->state], rr_state_names[state]);
+
+ rr->state = state;
+}
+
+static void start_rr_t3122(struct gsm_rrlayer *rr, int sec, int micro)
+{
+ DEBUGP(DRR, "starting T3122 with %d seconds\n", current, sec);
+ rr->t3122.cb = timeout_rr_t3122;
+ rr->t3122.data = rr;
+ bsc_schedule_timer(&rr->t3122, sec, micro);
+}
+
+static void start_rr_t3126(struct gsm_rrlayer *rr, int sec, int micro)
+{
+ DEBUGP(DRR, "starting T3126 with %d seconds\n", current, sec);
+ rr->t3126.cb = timeout_rr_t3126;
+ rr->t3126.data = rr;
+ bsc_schedule_timer(&rr->t3126, sec, micro);
+}
+
+static void stop_rr_t3126(struct gsm_rrlayer *rr)
+{
+ if (bsc_timer_pending(rr->t3126)) {
+ DEBUGP(DRR, "stopping pending timer T3126\n");
+ bsc_del_timer(&rr->t3126);
+ }
+}
+
+static void stop_rr_t3122(struct gsm_rrlayer *rr)
+{
+ if (bsc_timer_pending(rr->t3122)) {
+ DEBUGP(DRR, "stopping pending timer T3122\n");
+ bsc_del_timer(&rr->t3122);
+ }
+ rr->t3122_running = 0;
+}
+
+static int gsm_match_mi(struct osmocom_ms *ms, u_int8_t mi)
+{
+ char imsi[16];
+ u_int32_t tmsi;
+
+ if (mi[0] < 1)
+ return 0;
+ mi_type = mi[1] & GSM_MI_TYPE_MASK;
+ switch (mi_type) {
+ case GSM_MI_TYPE_TMSI:
+ if (mi[0] < 5)
+ return;
+ memcpy(&tmsi, mi+2, 4);
+ if (ms->subscr.tmsi == ntohl(tmsi)
+ && ms->subscr.tmsi_valid)
+ return 1;
+ break;
+ case GSM_MI_TYPE_IMSI:
+ gsm48_mi_to_string(imsi, sizeof(imsi), mi + 1, mi[0]);
+ if (!strcmp(imsi, ms->subscr.imsi))
+ return 1;
+ break;
+ default:
+ DEBUGP(DRR, "paging with unsupported MI type %d.\n", mi_type);
+ }
+
+ return 0;
+}
+
+static int gsm_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *req)
+{
+ struct gsm_rrlayer *rr = ms->rrlayer;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (rr->cr_hist[i] >= 0
+ && ref->ra == rr->cr_hist[i]) {
+ todo: match timeslot
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int gsm_rr_tx_chan_req(struct osmocom_ms *ms, int cause)
+{
+ struct gsm_rrlayer *rr = ms->rrlayer;
+
+ /* 3.3.1.1.2 */
+ new_rr_state(rr, GSM_RRSTATE_CONN_PEND);
+
+ /* number of retransmissions (without first transmission) */
+ rr->n_chan_req = ms->si.max_retrans;
+
+ /* generate CHAN REQ (9.1.8) */
+ chan_req = random();
+ switch (cause) {
+ case RR_EST_CAUSE_EMERGENCY:
+ /* 101xxxxx */
+ chan_req &= 0x1f;
+ chan_req |= 0xa0;
+ DEBUGP(DRR, "CHANNEL REQUEST: %02x (Emergency call)\n", chan_req);
+ break;
+ case RR_EST_CAUSE_REESTAB_TCH_F:
+ chan_req &= 0x1f;
+ chan_req |= 0xc0;
+ DEBUGP(DRR, "CHANNEL REQUEST: %02x (re-establish TCH/F)\n", chan_req);
+ break;
+ case RR_EST_CAUSE_REESTAB_TCH_H:
+ if (ms->si.neci) {
+ chan_req &= 0x03;
+ chan_req |= 0x68;
+ DEBUGP(DRR, "CHANNEL REQUEST: %02x (re-establish TCH/H with NECI)\n", chan_req);
+ } else {
+ chan_req &= 0x1f;
+ chan_req |= 0xc0;
+ DEBUGP(DRR, "CHANNEL REQUEST: %02x (re-establish TCH/H no NECI)\n", chan_req);
+ }
+ break;
+ case RR_EST_CAUSE_REESTAB_2_TCH_H:
+ if (ms->si.neci) {
+ chan_req &= 0x03;
+ chan_req |= 0x6c;
+ DEBUGP(DRR, "CHANNEL REQUEST: %02x (re-establish TCH/H+TCH/H with NECI)\n", chan_req);
+ } else {
+ chan_req &= 0x1f;
+ chan_req |= 0xc0;
+ DEBUGP(DRR, "CHANNEL REQUEST: %02x (re-establish TCH/H+TCH/H no NECI)\n", chan_req);
+ }
+ break;
+ case RR_EST_CAUSE_ANS_PAG_ANY:
+ chan_req &= 0x1f;
+ chan_req |= 0x80;
+ DEBUGP(DRR, "CHANNEL REQUEST: %02x (PAGING Any channel)\n", chan_req);
+ break;
+ case RR_EST_CAUSE_ANS_PAG_SDCCH:
+ chan_req &= 0x0f;
+ chan_req |= 0x10;
+ DEBUGP(DRR, "CHANNEL REQUEST: %02x (PAGING SDCCH)\n", chan_req);
+ break;
+ case RR_EST_CAUSE_ANS_PAG_TCH_F:
+ /* ms supports no dual rate */
+ chan_req &= 0x1f;
+ chan_req |= 0x80;
+ DEBUGP(DRR, "CHANNEL REQUEST: %02x (PAGING TCH/F)\n", chan_req);
+ break;
+ case RR_EST_CAUSE_ANS_PAG_TCH_ANY:
+ /* ms supports no dual rate */
+ chan_req &= 0x1f;
+ chan_req |= 0x80;
+ DEBUGP(DRR, "CHANNEL REQUEST: %02x (PAGING TCH/H or TCH/F)\n", chan_req);
+ break;
+ case RR_EST_CAUSE_ORIG_TCHF:
+ /* ms supports no dual rate */
+ chan_req &= 0x1f;
+ chan_req |= 0xe0;
+ DEBUGP(DRR, "CHANNEL REQUEST: %02x (Orig TCH/F)\n", chan_req);
+ break;
+ case RR_EST_CAUSE_LOC_UPD:
+ if (ms->si.neci) {
+ chan_req &= 0x0f;
+ chan_req |= 0x00;
+ DEBUGP(DRR, "CHANNEL REQUEST: %02x (Location Update with NECI)\n", chan_req);
+ } else {
+ chan_req &= 0x1f;
+ chan_req |= 0x00;
+ DEBUGP(DRR, "CHANNEL REQUEST: %02x (Location Update no NECI)\n", chan_req);
+ }
+ break;
+ case RR_EST_CAUSE_OTHER_SDCCH:
+ if (ms->si.neci) {
+ chan_req &= 0x0f;
+ chan_req |= 0x01;
+ DEBUGP(DRR, "CHANNEL REQUEST: %02x (OHTER with NECI)\n", chan_req);
+ } else {
+ chan_req &= 0x1f;
+ chan_req |= 0xe0;
+ DEBUGP(DRR, "CHANNEL REQUEST: %02x (OTHER no NECI)\n", chan_req);
+ }
+ break;
+ default:
+ if (!rr->rr_est_req) /* no request from MM */
+ return -EINVAL;
+ struct gsm_rr rrmsg;
+
+ DEBUGP(DRR, "CHANNEL REQUEST: with unknown establishment cause: %d\n", rrmsg->cause);
+ memset(rrmsg, 0, sizeof(rrmsg));
+ rrmsg->rr_cause = RR_REL_CAUSE_UNDEFINED;
+ rr_recvmsg(ms, RR_REL_IND, rrmsg);
+ new_rr_state(rr, GSM_RRSTATE_IDLE);
+ return -EINVAL;
+ }
+
+ rr->wait_assign = 1;
+
+ /* create and send RACH msg */
+ memset(&dlmsg, 0, sizeof(dlmsg));
+ dlmsg->msg = msgb_alloc(1, "CHAN_REQ");
+ if (!dlmsg->msg)
+ return -ENOMEM;
+ *msgb_put(dlmsg->msg, 1) = chan_req;
+ rr->chan_req = chan_req;
+ t = ms->si.tx_integer;
+ if (t < 8)
+ t = 8;
+ dlmsg->delay = random() % t;
+ rr->cr_hist[3] = -1;
+ rr->cr_hist[2] = -1;
+ rr->cr_hist[1] = chan_req;
+ return gsm_send_dl(ms, DL_RANDOM_ACCESS_REQ, dlmsg);
+}
+
+static int gsm_rr_est_req(struct osmocom_ms *ms, struct gsm_rr *rrmsg)
+{
+ struct gsm_rrlayer *rr = ms->rrlayer;
+ struct gsm_dl dlmsg;
+
+ /* 3.3.1.1.3.2 */
+ if (rr->t3122_running) {
+ if (rrmsg->cause != RR_EST_CAUSE_EMERGENCY) {
+ struct gsm_rr newmsg;
+
+ memset(newmsg, 0, sizeof(newmsg));
+ newmsg->rr_cause = RR_REL_CAUSE_T3122_PENDING;
+ return rr_recvmsg(ms, RR_REL_IND, newmsg);
+ } else
+ stop_rr_t3122(rr);
+ }
+
+ bsc_schedule_timer(&rr->t3122, sec, micro);
+ /* 3.3.1.1.1 */
+ if (rrmsg->cause != RR_EST_CAUSE_EMERGENCY) {
+ if (!(ms->access_class & ms->si.access_class)) {
+ reject:
+ if (!ms->opt.access_class_override) {
+ struct gsm_rr newmsg;
+
+ memset(newmsg, 0, sizeof(newmsg));
+ newmsg->rr_cause = RR_REL_CAUSE_NOT_AUTHORIZED;
+ return rr_recvmsg(ms, RR_REL_IND, newmsg);
+ }
+ }
+ } else {
+ if (!(ms->access_class & ms->si.access_class)
+ && !ms->si.emergency)
+ goto reject;
+ }
+
+ /* requested by RR */
+ rr->rr_est_req = 1;
+
+ /* store REQUEST message */
+ rr->rr_est_msg = rrmsg->msg;
+
+ return gsm_rr_tx_chan_req(ms, rrmsg->cause);
+}
+
+static int gsm_rr_data_req(struct osmocom_ms *ms, struct gsm_rr *rrmsg)
+{
+ struct gsm_dl dlmsg;
+
+ /* 3.4.2 */
+ memset(&dlmsg, 0, sizeof(dlmsg));
+ dlmsg->msg = rrmsg->msg;
+ return gsm_send_dl(ms, DL_RELEASE_REQ, dlmsg);
+}
+
+static int gsm_rr_abort_req(struct osmocom_ms *ms, struct gsm_rr *rrmsg)
+{
+ struct gsm_rrlayer *rr = ms->rrlayer;
+ stop_rr_t3126(rr);
+ if (rr->state == GSM_RRSTATE_DEDICATED) {
+ struct gsm_dl dlmsg;
+
+ memset(&dlmsg, 0, sizeof(dlmsg));
+ return gsm_send_dl(ms, DL_RELEASE_REQ, dlmsg);
+ }
+ new_rr_state(rr, GSM_RRSTATE_IDLE);
+}
+
+static int gsm_rr_act_req(struct osmocom_ms *ms, struct gsm_rr *rrmsg)
+{
+}
+
+/* state trasitions for radio ressource messages (upper layer) */
+static struct rrdownstate {
+ u_int32_t states;
+ int type;
+ int (*rout) (struct osmocom_ms *ms, struct gsm_dl *rrmsg);
+} rrdownstatelist[] = {
+ {SBIT(GSM_RRSTATE_IDLE), /* 3.3.1.1 */
+ RR_EST_REQ, gsm_rr_est_req},
+ {SBIT(GSM_RRSTATE_DEDICATED), /* 3.4.2 */
+ RR_DATA_REQ, gsm_rr_data_req},
+ {SBIT(GSM_RRSTATE_CONN_PEND) | SBIT(GSM_RRSTATE_DEDICATED),
+ RR_ABORT_REQ, gsm_rr_abort_req},
+ {SBIT(GSM_RRSTATE_DEDICATED),
+ RR_ACT_REQ, gsm_rr_act_req},
+};
+
+#define RRDOWNSLLEN \
+ (sizeof(rrdownstatelist) / sizeof(struct rrdownstate))
+
+static int gsm_send_rr(struct osmocom_ms *ms, struct gsm_rr *rrmsg)
+{
+ int msg_type = rrmsg->msg_type;
+
+ DEBUGP(DRR, "(ms %s) Sending '%s' to DL in state %s\n", ms->name,
+ gsm0408_rr_msg_names[msg_type], mm_state_names[mm->state]);
+
+ /* find function for current state and message */
+ for (i = 0; i < RRDOWNSLLEN; i++)
+ if ((msg_type == rrdownstatelist[i].type)
+ && ((1 << mm->state) & rrdownstatelist[i].states))
+ break;
+ if (i == RRDOWNSLLEN) {
+ DEBUGP(DRR, "Message unhandled at this state.\n");
+ return 0;
+ }
+
+ rc = rrdownstatelist[i].rout(ms, dlmsg);
+
+ return rc;
+}
+
+static int gsm_rr_rx_pag_req_1(struct osmocom_ms *ms, struct gsm_msgb *msg)
+{
+ struct gsm_rrlayer *rr = ms->rrlayer;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ int chan_first, chan_second;
+
+ /* 3.3.1.1.2: ignore paging while establishing */
+ if (rr->state != GSM_RRSTATE_IDLE)
+ return 0;
+
+ if (payload_len < 1 + 2) {
+ short:
+ DEBUGP(DRR, "Short read of paging request 1 message .\n");
+ return -EINVAL;
+ }
+
+ /* channel needed */
+ chan_first = *gh->data & 0x03;
+ chan_second = (*gh->data >> 2) & 0x03;
+ /* first MI */
+ mi = gh->data + 1;
+ if (payload_len - 1 < mi[0] + 1)
+ goto short;
+ if (gsm_match_mi(ms, mi) > 0)
+ return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_first]);
+ /* second MI */
+ payload_len -= 1 + mi[0] - 1;
+ mi = gh->data + 1 + mi[0] + 1;
+ if (payload_len < 3)
+ return 0;
+ if (mi[0] != GSM48_IE_MOBILE_ID)
+ return 0;
+ if (payload_len < mi[1] + 2)
+ goto short;
+ if (gsm_match_mi(ms, mi + 1) > 0)
+ return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_second]);
+
+ return 0;
+}
+
+static int gsm_rr_rx_pag_req_2(struct osmocom_ms *ms, struct gsm_msgb *msg)
+{
+ struct gsm_rrlayer *rr = ms->rrlayer;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ u_int32_t tmsi;
+ int chan_first, chan_second, chan_third;
+
+ /* 3.3.1.1.2: ignore paging while establishing */
+ if (rr->state != GSM_RRSTATE_IDLE)
+ return 0;
+
+ if (payload_len < 1 + 4 + 4) {
+ short:
+ DEBUGP(DRR, "Short read of paging request 2 message .\n");
+ return -EINVAL;
+ }
+
+ /* channel needed */
+ chan_first = *gh->data & 0x03;
+ chan_second = (*gh->data >> 2) & 0x03;
+ /* first MI */
+ mi = gh->data + 1;
+ memcpy(&tmsi, mi, 4);
+ if (ms->subscr.tmsi == ntohl(tmsi)
+ && ms->subscr.tmsi_valid)
+ return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_first]);
+ /* second MI */
+ mi = gh->data + 1 + 4;
+ memcpy(&tmsi, mi, 4);
+ if (ms->subscr.tmsi == ntohl(tmsi)
+ && ms->subscr.tmsi_valid)
+ return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_second]);
+ /* third MI */
+ payload_len -= 1 + 4 + 4;
+ mi = gh->data + 1 + 4 + 4;
+ if (payload_len < 3)
+ return 0;
+ if (mi[0] != GSM48_IE_MOBILE_ID)
+ return 0;
+ if (payload_len < mi[1] + 2 + 1) /* must include "channel needed" */
+ goto short;
+ chan_third = mi[mi[1] + 2] & 0x03; /* channel needed */
+ if (gsm_match_mi(ms, mi + 1) > 0)
+ return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_third]);
+
+ return 0;
+}
+
+static int gsm_rr_rx_pag_req_3(struct osmocom_ms *ms, struct gsm_msgb *msg)
+{
+ struct gsm_rrlayer *rr = ms->rrlayer;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ u_int32_t tmsi;
+ int chan_first, chan_second, chan_third, chan_fourth;
+
+ /* 3.3.1.1.2: ignore paging while establishing */
+ if (rr->state != GSM_RRSTATE_IDLE)
+ return 0;
+
+ if (payload_len < 1 + 4 + 4 + 4 + 4 + 1) { /* must include "channel needed" */
+ short:
+ DEBUGP(DRR, "Short read of paging request 3 message .\n");
+ return -EINVAL;
+ }
+
+ /* channel needed */
+ chan_first = *gh->data & 0x03;
+ chan_second = (*gh->data >> 2) & 0x03;
+ chan_third = gh->data[1 + 4 + 4 + 4 + 4] & 0x03;
+ chan_fourth = (gh->data[1 + 4 + 4 + 4 + 4] >> 2) & 0x03;
+ /* first MI */
+ mi = gh->data + 1;
+ memcpy(&tmsi, mi, 4);
+ if (ms->subscr.tmsi == ntohl(tmsi)
+ && ms->subscr.tmsi_valid)
+ return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_first]);
+ /* second MI */
+ mi = gh->data + 1 + 4;
+ memcpy(&tmsi, mi, 4);
+ if (ms->subscr.tmsi == ntohl(tmsi)
+ && ms->subscr.tmsi_valid)
+ return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_second]);
+ /* thrid MI */
+ mi = gh->data + 1 + 4 + 4;
+ memcpy(&tmsi, mi, 4);
+ if (ms->subscr.tmsi == ntohl(tmsi)
+ && ms->subscr.tmsi_valid)
+ return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_third]);
+ /* fourth MI */
+ mi = gh->data + 1 + 4 + 4 + 4;
+ memcpy(&tmsi, mi, 4);
+ if (ms->subscr.tmsi == ntohl(tmsi)
+ && ms->subscr.tmsi_valid)
+ return gsm_rr_tx_chan_req(ms, gsm_rr_chan2cause[chan_fourth]);
+
+ return 0;
+}
+
+static int gsm_rr_dl_est(struct osmocom_ms *ms, struct gsm_msgb *msg)
+{
+ struct gsm_rrlayer *rr = ms->rrlayer;
+
+ /* 3.3.1.1.3.1 */
+ stop_rr_t3126(rr);
+
+ /* flush pending RACH requests */
+ rr->n_chan_req = 0; // just to be safe
+ memset(&dlmsg, 0, sizeof(dlmsg));
+ gsm_send_dl(ms, DL_RANDOM_ACCESS_FLU, dlmsg);
+
+ /* send DL_EST_REQ */
+ memset(&dlmsg, 0, sizeof(dlmsg));
+ memcpy(dlmsg->channel_description, rr->channel_description, 3);
+ memcpy(dlmsg->mobile_alloc_lv, rr->mobile_alloc_lv, 9);
+ todo starting time
+ if (rr->rr_est_msg) {
+ dlmsg->msg = rr->rr_est_msg;
+ rr->rr_est_msg= 0;
+ } else {
+ todo paging response
+ }
+ gsm_send_dl(ms, DL_ESTABLISH_REQ, dlmsg);
+}
+
+static int gsm_rr_rx_imm_ass(struct osmocom_ms *ms, struct gsm_msgb *msg)
+{
+ struct gsm_rrlayer *rr = ms->rrlayer;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct gsm48_req_ref *ref;
+
+ /* 3.3.1.1.2: ignore assignment while idle */
+ if (rr->state != GSM_RRSTATE_CONN_PEND || !rr->wait_assign)
+ return 0;
+
+ if (payload_len < 1 + 3 + 3 + 1 + 1) {
+ short:
+ DEBUGP(DRR, "Short read of immediate assignment message.\n");
+ return -EINVAL;
+ }
+ if (*gh->data[1 + 3 + 3 + 1] > 8) {
+ DEBUGP(DRR, "moble allocation in immediate assignment too large.\n");
+ return -EINVAL;
+ }
+
+ /* request ref */
+ if (gsm_match_ra(ms, (struct gsm48_req_ref *)(gh->data + 1 + 3))) {
+ /* channel description */
+ memcpy(rr->chan_descr, gh->data + 1, 3);
+ /* timing advance */
+ rr->timing_advance = *gh->data[1 + 3 + 3];
+ /* mobile allocation */
+ memcpy(rr->mobile_alloc_lv, gh->data + 1 + 3 + 3 + 1, *gh->data[1 + 3 + 3 + 1] + 1);
+ rr->wait_assing = 0;
+ return gsm_rr_dl_est(ms);
+ }
+
+ return 0;
+}
+
+static int gsm_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct gsm_msgb *msg)
+{
+ struct gsm_rrlayer *rr = ms->rrlayer;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+
+ /* 3.3.1.1.2: ignore assignment while idle */
+ if (rr->state != GSM_RRSTATE_CONN_PEND || !rr->wait_assign)
+ return 0;
+
+ if (payload_len < 1 + 3 + 3 + 1 + 3 + 3 + 1 + 1) {
+ short:
+ DEBUGP(DRR, "Short read of immediate assignment extended message.\n");
+ return -EINVAL;
+ }
+ if (*gh->data[1 + 3 + 3 + 1 + 3 + 3 + 1] > 4) {
+ DEBUGP(DRR, "moble allocation in immediate assignment extended too large.\n");
+ return -EINVAL;
+ }
+
+ /* request ref */
+ if (gsm_match_ra(ms, (struct gsm48_req_ref *)(gh->data + 1 + 3))) {
+ /* channel description */
+ memcpy(rr->chan_descr, gh->data + 1, 3);
+ /* timing advance */
+ rr->timing_advance = *gh->data[1 + 3 + 3];
+ /* mobile allocation */
+ memcpy(rr->mobile_alloc_lv, gh->data + 1 + 3 + 3 + 1 + 3 + 3 + 1, *gh->data[1 + 3 + 3 + 1 + 3 + 3 + 1] + 1);
+ rr->wait_assing = 0;
+ return gsm_rr_dl_est(ms);
+ }
+ /* request ref 2 */
+ if (gsm_match_ra(ms, (struct gsm48_req_ref *)(gh->data + 1 + 3 + 3 + 1 + 3))) {
+ /* channel description */
+ memcpy(rr->chan_descr, gh->data + 1 + 3 + 3 + 1, 3);
+ /* timing advance */
+ rr->timing_advance = *gh->data[1 + 3 + 3 + 1 + 3 + 3];
+ /* mobile allocation */
+ memcpy(rr->mobile_alloc_lv, gh->data + 1 + 3 + 3 + 1 + 3 + 3 + 1, *gh->data[1 + 3 + 3 + 1 + 3 + 3 + 1] + 1);
+ rr->wait_assing = 0;
+ return gsm_rr_dl_est(ms);
+ }
+
+ return 0;
+}
+
+static int gsm_rr_rx_imm_ass_rej(struct osmocom_ms *ms, struct gsm_msgb *msg)
+{
+ struct gsm_rrlayer *rr = ms->rrlayer;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ int i;
+
+ /* 3.3.1.1.2: ignore assignment while idle */
+ if (rr->state != GSM_RRSTATE_CONN_PEND || !rr->wait_assign)
+ return 0;
+
+ if (payload_len < 1 + 4 * (3 + 1)) {
+ short:
+ DEBUGP(DRR, "Short read of immediate assignment reject message.\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (gsm_match_ra(ms, (struct gsm48_req_ref *)(gh->data + 1 + i * (3 + 1)))) {
+ rr->wait_assing = 0;
+ /* wait indication */
+ t3122_value = gh->data[1 + i * (3 + 1) + 3];
+ if (t3122_value) {
+ start_rr_t3122(rr, t3122_value);
+ t3122_running = 1;
+ }
+ /* release */
+ if (rr->rr_est_req) {
+ struct gsm_rr rrmsg;
+
+ memset(rrmsg, 0, sizeof(rrmsg));
+ rrmsg->rr_cause = RR_REL_CAUSE_CHANNEL_REJECT;
+ rr_recvmsg(ms, RR_REL_IND, rrmsg);
+ }
+ new_rr_state(rr, GSM_RRSTATE_IDLE);
+ }
+ }
+
+ return 0;
+}
+
+static int gsm_rr_unit_data_ind(struct osmocom_ms *ms, struct gsm_dl *dlmsg)
+{
+ struct gsm_rrlayer *rr = ms->rrlayer;
+ struct gsm48_hdr *gh = msgb_l3(dlmsg->msg);
+
+ switch (gh->msg_type) {
+ case GSM48_MT_RR_PAG_REQ_1:
+ return gsm_rr_rx_pag_req_1(ms, dlmsg->msg);
+ case GSM48_MT_RR_PAG_REQ_2:
+ return gsm_rr_rx_pag_req_2(ms, dlmsg->msg);
+ case GSM48_MT_RR_PAG_REQ_3:
+ return gsm_rr_rx_pag_req_3(ms, dlmsg->msg);
+ case GSM48_MT_RR_IMM_ASS:
+ return gsm_rr_rx_imm_ass(ms, dlmsg->msg);
+ case GSM48_MT_RR_IMM_ASS_EXT:
+ return gsm_rr_rx_imm_ass_ext(ms, dlmsg->msg);
+ case GSM48_MT_RR_IMM_ASS_REJ:
+ return gsm_rr_rx_imm_ass_rej(ms, dlmsg->msg);
+ default:
+ DEBUGP(DRR, "Message type %d unknown.\n", gh->msg_type);
+ return -EINVAL;
+ }
+}
+
+static int gsm_rr_data_ind(struct osmocom_ms *ms, struct gsm_dl *dlmsg)
+{
+ struct gsm_rr rrmsg;
+
+ /* 3.4.2 */
+ memset(&rrmsg, 0, sizeof(rrmsg));
+ rrmsg->msg = dlmsg->msg;
+ return rr_recv_msg(ms, DL_RELEASE_REQ, rrmsg);
+}
+
+static int gsm_rr_estab_cnf(struct osmocom_ms *ms, struct gsm_dl *dlmsg)
+{
+ struct gsm_rr rrmsg;
+
+ if (rr->state == GSM_RRSTATE_IDLE) {
+ struct gsm_dl dlmsg;
+
+ memset(&dlmsg, 0, sizeof(dlmsg));
+ return gsm_send_dl(ms, DL_RELEASE_REQ, dlmsg);
+ }
+
+ /* 3.3.1.1.4 */
+ new_rr_state(rr, GSM_RRSTATE_DEDICATED);
+
+ memset(rrmsg, 0, sizeof(rrmsg));
+ return rr_recvmsg(ms, (rr->rr_est_req) ? RR_EST_CNF : RR_EST_IND, rrmsg);
+}
+
+static int gsm_rr_suspend_cnf(struct osmocom_ms *ms, struct gsm_dl *dlmsg)
+{
+}
+
+static int gsm_rr_suspend_cnf(struct osmocom_ms *ms, struct gsm_dl *dlmsg)
+{
+}
+
+static int gsm_rr_connect_cnf(struct osmocom_ms *ms, struct gsm_dl *dlmsg)
+{
+}
+
+static int gsm_rr_rel_ind(struct osmocom_ms *ms, struct gsm_dl *dlmsg)
+{
+}
+
+static int gsm_rr_rel_cnf(struct osmocom_ms *ms, struct gsm_dl *dlmsg)
+{
+}
+
+static int gsm_rr_rand_acc_cnf(struct osmocom_ms *ms, struct gsm_dl *dlmsg)
+{
+ struct gsm_rrlayer *rr = ms->rrlayer;
+ int s;
+
+ if (!rr->n_chan_req) {
+ start_rr_t3126(rr, GSM_T3126_MS);
+ return 0;
+ }
+ rr->n_chan_req--;
+
+ switch(ms->si.tx_integer) {
+ case 3: case 8: case 14: case 50:
+ if (ms->si.bcch_type == GSM_NON_COMBINED_CCCH)
+ s = 55;
+ else
+ s = 41;
+ case 4: case 9: case 16:
+ if (ms->si.bcch_type == GSM_NON_COMBINED_CCCH)
+ s = 76;
+ else
+ s = 52;
+ case 5: case 10: case 20:
+ if (ms->si.bcch_type == GSM_NON_COMBINED_CCCH)
+ s = 109;
+ else
+ s = 58;
+ case 6: case 11: case 25:
+ if (ms->si.bcch_type == GSM_NON_COMBINED_CCCH)
+ s = 163;
+ else
+ s = 86;
+ default:
+ if (ms->si.bcch_type == GSM_NON_COMBINED_CCCH)
+ s = 217;
+ else
+ s = 115;
+
+ /* resend chan_req */
+ memset(&dlmsg, 0, sizeof(dlmsg));
+ dlmsg->msg = msgb_alloc(1, "CHAN_REQ");
+ if (!dlmsg->msg)
+ return -ENOMEM;
+ *msgb_put(dlmsg->msg, 1) = rr->chan_req;
+ dlmsg->delay = (random() % ms->si.tx_integer) + s;
+ rr->cr_hist[3] = rr->cr_hist[2];
+ rr->cr_hist[2] = rr->cr_hist[1];
+ rr->cr_hist[1] = chan_req;
+ return gsm_send_dl(ms, DL_RANDOM_ACCESS_REQ, dlmsg);
+
+}
+
+static int gsm_rr_mdl_error_ind(struct osmocom_ms *ms, struct gsm_dl *dlmsg)
+{
+}
+
+/* state trasitions for link layer messages (lower layer) */
+static struct dldatastate {
+ u_int32_t states;
+ int type;
+ int (*rout) (struct osmocom_ms *ms, struct gsm_dl *dlmsg);
+} dldatastatelist[] = {
+ {SBIT(GSM_RRSTATE_IDLE) | SBIT(GSM_RRSTATE_CONN_PEND),
+ DL_UNIT_DATA_IND, gsm_rr_unit_data_ind},
+ {SBIT(GSM_RRSTATE_DEDICATED), /* 3.4.2 */
+ DL_DATA_IND, gsm_rr_data_ind},
+ {SBIT(GSM_RRSTATE_IDLE) | SBIT(GSM_RRSTATE_CONN_PEND),
+ DL_ESTABLISH_CNF, gsm_rr_estab_cnf},
+ {SBIT(GSM_RRSTATE),
+ DL_SUSPEND_CNF, gsm_rr_suspend_cnf},
+ {SBIT(GSM_RRSTATE),
+ DL_RESUME_CNF, gsm_rr_suspend_cnf},
+ {SBIT(GSM_RRSTATE),
+ DL_CONNECT_CNF, gsm_rr_connect_cnf},
+ {SBIT(GSM_RRSTATE),
+ DL_RELEASE_IND, gsm_rr_rel_ind},
+ {SBIT(GSM_RRSTATE),
+ DL_RELEASE_CNF, gsm_rr_rel_cnf},
+ {SBIT(GSM_RRSTATE_CONN_PEND), /* 3.3.1.1.2 */
+ DL_RANDOM_ACCESS_CNF, gsm_rr_rand_acc_cnf},
+ {SBIT(GSM_RRSTATE),
+ MDL_ERROR_IND, gsm_rr_mdl_error_ind},
+};
+
+#define DLDATASLLEN \
+ (sizeof(dldatastatelist) / sizeof(struct dldatastate))
+
+static int gsm_rcv_dl(struct osmocom_ms *ms, struct gsm_dl *dlmsg)
+{
+ int msg_type = dlmsg->msg_type;
+
+ DEBUGP(DRR, "(ms %s) Received '%s' from DL in state %s\n", ms->name,
+ gsm0408_dl_msg_names[msg_type], mm_state_names[mm->state]);
+
+ /* find function for current state and message */
+ for (i = 0; i < DLDATASLLEN; i++)
+ if ((msg_type == dldatastatelist[i].type)
+ && ((1 << mm->state) & dldatastatelist[i].states))
+ break;
+ if (i == DLDATASLLEN) {
+ DEBUGP(DRR, "Message unhandled at this state.\n");
+ return 0;
+ }
+
+ rc = dldatastatelist[i].rout(ms, dlmsg);
+
+ return rc;
+}
+
+static void timeout_rr_t3122(void *arg)
+{
+ struct gsm_rrlayer *rr = arg;
+
+ rr->t3122_running = 0;
+}
+
+static void timeout_rr_t3126(void *arg)
+{
+ struct gsm_rrlayer *rr = arg;
+
+ if (rr->rr_est_req) {
+ struct gsm_rr rrmsg;
+
+ memset(rrmsg, 0, sizeof(rrmsg));
+ rrmsg->rr_cause = RR_REL_CAUSE_RA_FAILURE;
+ rr_recvmsg(ms, RR_REL_IND, rrmsg);
+ }
+
+ new_rr_state(rr, GSM_RRSTATE_IDLE);
+}
+
+struct gsm_rrlayer *gsm_new_rr(struct osmocom_ms *ms)
+{
+ struct gsm_rrlayer *rr;
+
+ rr = calloc(1, sizeof(struct gsm_rrlayer));
+ if (!rr)
+ return NULL;
+ rr->ms = ms;
+
+ return;
+}
+
+void gsm_destroy_rr(struct gsm_rrlayer *rr)
+{
+ stop_rr_t3122(rr);
+ stop_rr_t3126(rr);
+alle timer gestoppt?:
+todo stop t3122 when cell change
+
+ memset(rr, 0, sizeof(struct gsm_rrlayer));
+ free(rr);
+
+ return;
+}
+
diff --git a/src/host/gsm48-andreas/issues.txt b/src/host/gsm48-andreas/issues.txt
new file mode 100644
index 0000000..e9b0586
--- /dev/null
+++ b/src/host/gsm48-andreas/issues.txt
@@ -0,0 +1,21 @@
+
+must be expanded:
+
+struct osmocom_ms:
+- struct rrlayer *
+- struct mmlayer *
+- transaction list
+- struct phone_options
+- struct system_infos (collected system infos)
+
+DL_RANDOM_ACCESS_REQ requires a delay of RACH slots.
+DL_RANDOM_ACCESS_FLU used to flush pending RACH requests
+
+must random value in chan_request change on every resend or only on every RR
+establishment? (currently it changes on every RR establishment only.)
+
+send und receive name concept:
+- sending to other layer
+- receiving from other layer
+- de-queue when sending to upper layer
+