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