diff options
Diffstat (limited to 'src/host/gsm48-andreas/gsm48_cc.c')
-rw-r--r-- | src/host/gsm48-andreas/gsm48_cc.c | 1748 |
1 files changed, 0 insertions, 1748 deletions
diff --git a/src/host/gsm48-andreas/gsm48_cc.c b/src/host/gsm48-andreas/gsm48_cc.c deleted file mode 100644 index 51bd663f..00000000 --- a/src/host/gsm48-andreas/gsm48_cc.c +++ /dev/null @@ -1,1748 +0,0 @@ -/* - * (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. - * - */ - -/* - * timers - */ - -/* 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; - } -} - -/* 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 - */ - -/* push MMCC header and send to MM */ -static int gsm48_cc_to_mm(struct msgb *msg, struct gsm_trans *trans, int msg_type) -{ - struct osmocom_ms *ms = trans->ms; - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - struct gsm48_mmxx_hdr *mmh; - int emergency = 0; - - /* indicate emergency setup to MM layer */ - if (gh->msg_type == GSM48_MT_CC_EMERG_SETUP) - emergency = 1; - - /* push RR header */ - msgb_push(msg, sizeof(struct gsm48_mmxx_hdr)); - 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 (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 osmocom_ms *ms, 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(ms, trans, MNCC_REL_IND, &rel); -} - -/* sending status message in response to unknown message */ -static int gsm48_cc_tx_status(struct gsm_trans *trans, int cause) -{ - struct msgb *nmsg; - struct gsm48_hdr *gh; - u_int8_t *cause, *call_state; - - nmsg = gsm48_l3_msgb_alloc(); - if (!nmsg) - return -ENOMEM; - gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_STATUS; - - cause = msgb_put(nmsg, 3); - cause[0] = 2; - cause[1] = GSM48_CAUSE_CS_GSM | GSM48_CAUSE_LOC_USER; - cause[2] = 0x80 | cause; - - call_state = msgb_put(nmsg, 1); - 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); -} - -/* - * 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) -{ - struct msgb *nmsg; - struct gsm48_hdr *gh; - struct gsm_mncc *setup = &trans->cc.msg; - int rc, trans_id; - - nmsg = gsm48_l3_msgb_alloc(); - if (!nmsg) - return -ENOMEM; - gh = (struct gsm48_hdr *) msgb_put(nmsg, 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->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->ms, GSM48_PDISC_CC, 0); - if (trans_id < 0) { - /* no free transaction ID */ - rc = mncc_release_ind(trans->ms, trans, trans->callref, - GSM48_CAUSE_LOC_USER, - 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; - - /* 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 */ - 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); - /* 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 (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); - } - - new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED); - - 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 */ -static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *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_CONNECT_ACK; - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - 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) -{ - struct gsm_mncc *confirm = 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_CALL_CONF; - - new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF); - - /* bearer capability */ - if (confirm->fields & MNCC_F_BEARER_CAP) - gsm48_encode_bearer_cap(nmsg, 0, &confirm->bearer_cap); - /* cause */ - if (confirm->fields & MNCC_F_CAUSE) - gsm48_encode_cause(nmsg, 0, &confirm->cause); - /* cc cap */ - if (confirm->fields & MNCC_F_CCCAP) - gsm48_encode_cccap(nmsg, 0, &confirm->cccap); - - return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ); -} - -/* alerting message from upper layer */ -static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *alerting = 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_ALERTING; - - /* facility */ - if (alerting->fields & MNCC_F_FACILITY) - gsm48_encode_facility(nmsg, 0, &alerting->facility); - /* user-user */ - if (alerting->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(nmsg, 0, &alerting->useruser); - /* ss version */ - if (alerting->fields & MNCC_F_SSVERSION) - gsm48_encode_ssversion(nmsg, 0, &alerting->ssversion); - - new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED); - - return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ); -} - -/* connect message from upper layer */ -static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *connect = 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_CONNECT; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x313, GSM48_T313_MS); - - /* facility */ - if (connect->fields & MNCC_F_FACILITY) - gsm48_encode_facility(nmsg, 0, &connect->facility); - /* user-user */ - if (connect->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(nmsg, 0, &connect->useruser); - /* ss version */ - if (connect->fields & MNCC_F_SSVERSION) - gsm48_encode_ssversion(nmsg, 0, &connect->ssversion); - - new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST); - - 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) -{ - struct gsm_mncc *notify = 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_NOTIFY; - - /* notify */ - gsm48_encode_notify(nmsg, notify->notify); - - 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) -{ - struct gsm_mncc *dtmf = 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_START_DTMF; - - /* keypad */ - gsm48_encode_keypad(nmsg, dtmf->keypad); - - 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) -{ - 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_STOP_DTMF; - - return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ); -} - -/* 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->ms, trans, MNCC_STOP_DTMF_RSP, &dtmf); -} - -/* hold message from upper layer */ -static int gsm48_cc_tx_hold(struct gsm_trans *trans, void *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_HOLD; - - 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) -{ - 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->ms, 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; - } - gsm48_decode_cause(&hold.cause, gh->data); - - return mncc_recvmsg(trans->ms, trans, MNCC_HOLD_REJ, &hold); -} - -/* retrieve message from upper layer */ -static int gsm48_cc_tx_retrieve(struct gsm_trans *trans, void *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_RETRIEVE; - - 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) -{ - 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->ms, 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; - } - gsm48_decode_cause(&retrieve.cause, gh->data); - - return mncc_recvmsg(trans->ms, trans, MNCC_RETRIEVE_REJ, &retrieve); -} - -/* 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 *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_FACILITY; - - /* facility */ - gsm48_encode_facility(nmsg, 1, &fac->facility); - /* ss version */ - if (rel->fields & MNCC_F_SSVERSION) - gsm48_encode_ssversion(nmsg, 0, &rel->ssversion); - - 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) -{ - struct gsm_mncc *user = 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_USER_INFO; - - /* user-user */ - if (user->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(nmsg, 1, &user->useruser); - /* more data */ - if (user->more) - gsm48_encode_more(nmsg); - - 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) -{ - 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; - - gsm48_start_cc_timer(trans, 0x323, GSM48_T323_MS); - - /* bearer capability */ - gsm48_encode_bearer_cap(nmsg, 1, &modify->bearer_cap); - - new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY); - - return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ); -} - -/* 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); - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return mncc_recvmsg(trans->ms, 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; - 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); - } - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return mncc_recvmsg(trans->ms, trans, MNCC_MODIFY_REJ, &modify); -} - -/* 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->ms, trans, MNCC_MODIFY_IND, &modify); -} - -/* 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) -{ - 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); -} - -/* - * process handlers (call clearing) - */ - -/* 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 *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_DISCONNECT; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x305, GSM48_T305_MS); - - /* 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); - - return gsm48_cc_to_mm(nmsg, trans, MMCC_DATA_REQ); -} - -/* 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 *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_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); - - 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) -{ - struct gsm_mncc *rel = arg; - struct msgb *nmsg; - struct gsm48_mmxx_hdr *mmh; - 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_RELEASE_COMPL; - - trans->callref = 0; - - gsm48_stop_cc_timer(trans); - - /* 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); - - - /* release MM conn, got NULL state, free trans */ - return gsm48_rel_null_free(trans); -} - -/* 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; - gsm48_decode_cause(&disc.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - disc.fields |= MNCC_F_FACILITY; - gsm48_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; - gsm48_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->ms, 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; - struct msgb *nmsg; - struct gsm48_mmxx_hdr *mmh; - 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; - gsm48_decode_cause(&rel.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - rel.fields |= MNCC_F_FACILITY; - gsm48_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; - gsm48_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 */ - mncc_recvmsg(trans->ms, trans, MNCC_REL_CNF, &rel); - } else { - struct gsm_mncc rel2; - - /* forward cause only */ - memcpy(&rel2, &rel, sizeof(struct gsm_mncc)); - rel2.fields = MNCC_F_CAUSE; - gsm48_cc_tx_release_compl(trans, &rel); - - /* release indication */ - mncc_recvmsg(trans->ms, trans, MNCC_REL_IND, &rel); - } - - /* release MM conn, got NULL state, free trans */ - return gsm48_rel_null_free(trans); -} - -/* 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; - struct msgb *nmsg; - struct gsm48_mmxx_hdr *mmh; - 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; - gsm48_decode_cause(&rel.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - rel.fields |= MNCC_F_FACILITY; - gsm48_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; - gsm48_decode_useruser(&rel.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - - if (trans->callref) { - switch (trans->cc.state) { - case GSM_CSTATE_CALL_PRESENT: - mncc_recvmsg(trans->ms, trans, - MNCC_REJ_IND, &rel); - break; - case GSM_CSTATE_RELEASE_REQ: - mncc_recvmsg(trans->ms, trans, - MNCC_REL_CNF, &rel); - break; - default: - mncc_recvmsg(trans->ms, trans, - MNCC_REL_IND, &rel); - } - } - - /* release MM conn, got NULL state, free trans */ - return gsm48_rel_null_free(trans); -} - -/* - * state machines - */ - -/* 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}, -}; - -#define DOWNSLLEN \ - (sizeof(downstatelist) / sizeof(struct downstate)) - -int mncc_send(struct osmocom_ms *ms, int msg_type, void *arg) -{ - struct gsm_mncc *data = arg, rel; - int msg_type = data->msg_type; - - /* 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); - } - - /* 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); - } - } - - /* 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 rc; -} - -/* 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) | 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_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)) - -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)); - - DEBUGP(DMM, "(ms %s) Received '%s' in CC state %s\n", ms->name, - gsm48_cc_msg_name(msg_type), gsm48_cc_state_names[trans->state]); - - /* 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) { - 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); - - return rc; -} - -/* receive message from MM layer */ -static int gsm48_rcv_cc(struct osmocom_ms *ms, struct msgb *msg) -{ - struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; - int msg_type = mmh->msg_type; - int rc = 0; - - 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 (msg_type) { - case GSM48_MMCC_EST_IND: - /* data included */ - rc = gsm48_cc_data_ind(trans, msg); - break; - 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 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; - free_trans(trans); - break; - 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: - DEBUGP(DMM, "Message unhandled.\n"); - rc = -ENOTSUP; - } - - return rc; -} - |