From 4bfdfe7f70376612ad2343dd71e8b6ad52124ee6 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 10 Jun 2009 23:11:52 +0800 Subject: reworked MNCC codebase This is Harald's reworked MNCC base, slowly heading towards integration into master. The key changes are: * provide much more structure to the data in gsm_mncc * encode_* and decode_* functions now take a structure rather than tons of individual arguments (whose order nobody can remember) * make sure we don't have copies of the same code everywhere by introducing mncc_set_cause() and mncc_release_ind() * save horizontal screen space if possible * make sure we break lines > 80 characters --- openbsc/src/Makefile.am | 2 +- openbsc/src/bs11_config.c | 2 +- openbsc/src/bsc_hack.c | 3 +- openbsc/src/chan_alloc.c | 4 +- openbsc/src/gsm_04_08.c | 2467 ++++++++++++++++++++++++++++++++++------- openbsc/src/gsm_data.c | 10 +- openbsc/src/ipaccess-config.c | 2 +- openbsc/src/mncc.c | 382 +++++++ openbsc/src/paging.c | 6 +- openbsc/src/trau_mux.c | 22 +- openbsc/src/vty_interface.c | 3 + 11 files changed, 2463 insertions(+), 440 deletions(-) create mode 100644 openbsc/src/mncc.c (limited to 'openbsc/src') diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am index fbbbfdc94..f4b8e552d 100644 --- a/openbsc/src/Makefile.am +++ b/openbsc/src/Makefile.am @@ -5,7 +5,7 @@ sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config isdnsync noinst_LIBRARIES = libbsc.a libvty.a noinst_HEADERS = vty/cardshell.h -libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_04_08.c gsm_data.c \ +libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_04_08.c gsm_data.c mncc.c \ gsm_subscriber.c msgb.c select.c chan_alloc.c timer.c debug.c db.c \ gsm_04_11.c telnet_interface.c subchan_demux.c \ trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c tlv_parser.c \ diff --git a/openbsc/src/bs11_config.c b/openbsc/src/bs11_config.c index 656feb453..386b865ca 100644 --- a/openbsc/src/bs11_config.c +++ b/openbsc/src/bs11_config.c @@ -775,7 +775,7 @@ int main(int argc, char **argv) handle_options(argc, argv); - gsmnet = gsm_network_init(1, 1, 1, GSM_BTS_TYPE_BS11); + gsmnet = gsm_network_init(1, 1, 1, GSM_BTS_TYPE_BS11, NULL); if (!gsmnet) { fprintf(stderr, "Unable to allocate gsm network\n"); exit(1); diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c index 388840793..e41b0e382 100644 --- a/openbsc/src/bsc_hack.c +++ b/openbsc/src/bsc_hack.c @@ -995,7 +995,7 @@ static int bootstrap_network(void) } /* initialize our data structures */ - gsmnet = gsm_network_init(2, BTS_TYPE, MCC, MNC); + gsmnet = gsm_network_init(2, BTS_TYPE, MCC, MNC, mncc_recv); if (!gsmnet) return -ENOMEM; @@ -1188,6 +1188,7 @@ int main(int argc, char **argv) signal(SIGABRT, &signal_handler); while (1) { + bsc_upqueue(gsmnet); bsc_select_main(0); } } diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c index 77a4f57bc..96632bcb9 100644 --- a/openbsc/src/chan_alloc.c +++ b/openbsc/src/chan_alloc.c @@ -62,7 +62,7 @@ static const enum abis_nm_chan_comb chcomb4pchan[] = { /* FIXME: bounds check */ }; -/* Allocate a logical channel (TS) */ +/* Allocate a physical channel (TS) */ struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts, enum gsm_phys_chan_config pchan) { @@ -114,7 +114,7 @@ static const u_int8_t subslots_per_pchan[] = { [GSM_PCHAN_CCCH_SDCCH4] = 4, [GSM_PCHAN_TCH_F] = 1, [GSM_PCHAN_TCH_H] = 2, - [GSM_PCHAN_SDCCH8_SACCH8C] = 8. + [GSM_PCHAN_SDCCH8_SACCH8C] = 8, }; static struct gsm_lchan * diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c index 90b88dfb3..3aaa0f53c 100644 --- a/openbsc/src/gsm_04_08.c +++ b/openbsc/src/gsm_04_08.c @@ -113,6 +113,108 @@ static const char *rr_cause_names[] = { [GSM48_RR_CAUSE_PROT_ERROR_UNSPC] = "Protocol error unspecified", }; +static const char *cc_state_names[] = { + "NULL", + "INITIATED", + "illegal state 2", + "MO_CALL_PROC", + "CALL_DELIVERED", + "illegal state 5", + "CALL_PRESENT", + "CALL_RECEIVED", + "CONNECT_REQUEST", + "MO_TERM_CALL_CONF", + "ACTIVE", + "DISCONNECT_REQ", + "DISCONNECT_IND", + "illegal state 13", + "illegal state 14", + "illegal state 15", + "illegal state 16", + "illegal state 17", + "illegal state 18", + "RELEASE_REQ", + "illegal state 20", + "illegal state 21", + "illegal state 22", + "illegal state 23", + "illegal state 24", + "illegal state 25", + "MO_ORIG_MODIFY", + "MO_TERM_MODIFY", + "CONNECT_IND", + "illegal state 29", + "illegal state 30", + "illegal state 31", +}; + +static const char *cc_msg_names[] = { + "unknown 0x00", + "ALERTING", + "CALL_PROC", + "PROGRESS", + "ESTAB", + "SETUP", + "ESTAB_CONF", + "CONNECT", + "CALL_CONF", + "START_CC", + "unknown 0x0a", + "RECALL", + "unknown 0x0c", + "unknown 0x0d", + "EMERG_SETUP", + "CONNECT_ACK", + "USER_INFO", + "unknown 0x11", + "unknown 0x12", + "MODIFY_REJECT", + "unknown 0x14", + "unknown 0x15", + "unknown 0x16", + "MODIFY", + "HOLD", + "HOLD_ACK", + "HOLD_REJ", + "unknown 0x1b", + "RETR", + "RETR_ACK", + "RETR_REJ", + "MODIFY_COMPL", + "unknown 0x20", + "unknown 0x21", + "unknown 0x22", + "unknown 0x23", + "unknown 0x24", + "DISCONNECT", + "unknown 0x26", + "unknown 0x27", + "unknown 0x28", + "unknown 0x29", + "RELEASE_COMPL", + "unknown 0x2b", + "unknown 0x2c", + "RELEASE", + "unknown 0x2e", + "unknown 0x2f", + "unknown 0x30", + "STOP_DTMF", + "STOP_DTMF_ACK", + "unknown 0x33", + "STATUS_ENQ", + "START_DTMF", + "START_DTMF_ACK", + "START_DTMF_REJ", + "unknown 0x38", + "CONG_CTRL", + "FACILITY", + "unknown 0x3b", + "STATUS", + "unknown 0x3c", + "NOTIFY", + "unknown 0x3f", +}; + static char strbuf[64]; static const char *rr_cause_name(u_int8_t cause) @@ -186,6 +288,7 @@ int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi); static int gsm48_tx_simple(struct gsm_lchan *lchan, u_int8_t pdisc, u_int8_t msg_type); static void schedule_reject(struct gsm_lchan *lchan); +void free_trans(struct gsm_trans *trans); struct gsm_lai { u_int16_t mcc; @@ -206,6 +309,8 @@ void gsm0408_set_reject_cause(int cause) reject_cause = cause; } +static u_int32_t new_callref = 0x80000001; + static int authorize_subscriber(struct gsm_loc_updating_operation *loc, struct gsm_subscriber *subscriber) { @@ -265,6 +370,8 @@ static int gsm0408_authorize(struct gsm_lchan *lchan, struct msgb *msg) static int gsm0408_handle_lchan_signal(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { + struct gsm_trans *trans, *temp; + if (subsys != SS_LCHAN || signal != S_LCHAN_UNEXPECTED_RELEASE) return 0; @@ -275,6 +382,12 @@ static int gsm0408_handle_lchan_signal(unsigned int subsys, unsigned int signal, struct gsm_lchan *lchan = (struct gsm_lchan *)handler_data; release_loc_updating_req(lchan); + /* Free all transactions that are associated with the released lchan */ + llist_for_each_entry_safe(trans, temp, &lchan->ts->trx->bts->network->trans_list, entry) { + if (trans->lchan == lchan) + free_trans(trans); + } + return 0; } @@ -411,8 +524,7 @@ int encode_bcd_number(u_int8_t *bcd_lv, u_int8_t max_len, } /* decode 'bearer capability' */ -static int decode_bearer_cap(int *transfer, int *mode, int *coding, - int *radio, int *speech_ctm, int *speech_ver, +static int decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap, const u_int8_t *lv) { u_int8_t in_len = lv[0]; @@ -421,13 +533,13 @@ static int decode_bearer_cap(int *transfer, int *mode, int *coding, if (in_len < 1) return -EINVAL; - speech_ver[0] = -1; /* end of list, of maximum 7 values */ + bcap->speech_ver[0] = -1; /* end of list, of maximum 7 values */ /* octet 3 */ - *transfer = lv[1] & 0x07; - *mode = (lv[1] & 0x08) >> 3; - *coding = (lv[1] & 0x10) >> 4; - *radio = (lv[1] & 0x60) >> 5; + bcap->transfer = lv[1] & 0x07; + bcap->mode = (lv[1] & 0x08) >> 3; + bcap->coding = (lv[1] & 0x10) >> 4; + bcap->radio = (lv[1] & 0x60) >> 5; i = 1; s = 0; @@ -435,10 +547,10 @@ static int decode_bearer_cap(int *transfer, int *mode, int *coding, i++; /* octet 3a etc */ if (in_len < i) return 0; - speech_ver[s++] = lv[i] & 0x0f; - speech_ver[s] = -1; /* end of list */ + bcap->speech_ver[s++] = lv[i] & 0x0f; + bcap->speech_ver[s] = -1; /* end of list */ if (i == 2) /* octet 3a */ - *speech_ctm = (lv[i] & 0x20) >> 5; + bcap->speech_ctm = (lv[i] & 0x20) >> 5; if (s == 7) /* maximum speech versions + end of list */ return 0; } @@ -447,24 +559,23 @@ static int decode_bearer_cap(int *transfer, int *mode, int *coding, } /* encode 'bearer capability' */ -static int encode_bearer_cap(int lv_only, struct msgb *msg, int transfer, - int mode, int coding, int radio, int speech_ctm, - int *speech_ver) +static int encode_bearer_cap(struct msgb *msg, int lv_only, + const struct gsm_mncc_bearer_cap *bcap) { u_int8_t lv[32 + 1]; int i, s; - lv[1] = transfer; - lv[1] |= mode << 3; - lv[1] |= coding << 4; - lv[1] |= radio << 5; + lv[1] = bcap->transfer; + lv[1] |= bcap->mode << 3; + lv[1] |= bcap->coding << 4; + lv[1] |= bcap->radio << 5; i = 1; - for (s = 0; speech_ver[s] >= 0; s++) { + for (s = 0; bcap->speech_ver[s] >= 0; s++) { i++; /* octet 3a etc */ - lv[i] = speech_ver[s]; + lv[i] = bcap->speech_ver[s]; if (i == 2) /* octet 3a */ - lv[i] |= speech_ctm << 5; + lv[i] |= bcap->speech_ctm << 5; } lv[i] |= 0x80; /* last IE of octet 3 etc */ @@ -478,7 +589,7 @@ static int encode_bearer_cap(int lv_only, struct msgb *msg, int transfer, } /* decode 'call control cap' */ -static int decode_cccap(int *dtmf, int *pcp, const u_int8_t *lv) +static int decode_cccap(struct gsm_mncc_cccap *ccap, const u_int8_t *lv) { u_int8_t in_len = lv[0]; @@ -486,15 +597,15 @@ static int decode_cccap(int *dtmf, int *pcp, const u_int8_t *lv) return -EINVAL; /* octet 3 */ - *dtmf = lv[1] & 0x01; - *pcp = (lv[1] & 0x02) >> 1; + ccap->dtmf = lv[1] & 0x01; + ccap->pcp = (lv[1] & 0x02) >> 1; return 0; } /* decode 'called party BCD number' */ -static int decode_called(int *type, int *plan, char *number, - int number_len, const u_int8_t *lv) +static int decode_called(struct gsm_mncc_number *called, + const u_int8_t *lv) { u_int8_t in_len = lv[0]; @@ -502,27 +613,28 @@ static int decode_called(int *type, int *plan, char *number, return -EINVAL; /* octet 3 */ - *plan = lv[1] & 0x0f; - *type = (lv[1] & 0x70) >> 4; + called->plan = lv[1] & 0x0f; + called->type = (lv[1] & 0x70) >> 4; /* octet 4..N */ - decode_bcd_number(number, number_len, lv, 1); + decode_bcd_number(called->number, sizeof(called->number), lv, 1); return 0; } /* encode 'called party BCD number' */ -static int encode_called(struct msgb *msg, int type, int plan, char *number) +static int encode_called(struct msgb *msg, + const struct gsm_mncc_number *called) { u_int8_t lv[18]; int ret; /* octet 3 */ - lv[1] = plan; - lv[1] |= type << 4; + lv[1] = called->plan; + lv[1] |= called->type << 4; /* octet 4..N, octet 2 */ - ret = encode_bcd_number(lv, sizeof(lv), 1, number); + ret = encode_bcd_number(lv, sizeof(lv), 1, called->number); if (ret < 0) return ret; @@ -532,28 +644,28 @@ static int encode_called(struct msgb *msg, int type, int plan, char *number) } /* encode callerid of various IEs */ -static int encode_callerid(struct msgb *msg, int ie, int type, int plan, - int present, int screen, char *number) +static int encode_callerid(struct msgb *msg, int ie, + const struct gsm_mncc_number *callerid) { u_int8_t lv[13]; int h_len = 1; int ret; /* octet 3 */ - lv[1] = plan; - lv[1] |= type << 4; + lv[1] = callerid->plan; + lv[1] |= callerid->type << 4; - if (present || screen) { + if (callerid->present || callerid->screen) { /* octet 3a */ - lv[2] = screen; - lv[2] |= present << 5; + lv[2] = callerid->screen; + lv[2] |= callerid->present << 5; lv[2] |= 0x80; h_len++; } else lv[1] |= 0x80; /* octet 4..N, octet 2 */ - ret = encode_bcd_number(lv, sizeof(lv), h_len, number); + ret = encode_bcd_number(lv, sizeof(lv), h_len, callerid->number); if (ret < 0) return ret; @@ -563,8 +675,7 @@ static int encode_callerid(struct msgb *msg, int ie, int type, int plan, } /* decode 'cause' */ -static int decode_cause(int *location, int *coding, int *rec, int *rec_val, - int *value, u_char *diag, u_int *diag_len, +static int decode_cause(struct gsm_mncc_cause *cause, const u_int8_t *lv) { u_int8_t in_len = lv[0]; @@ -573,25 +684,25 @@ static int decode_cause(int *location, int *coding, int *rec, int *rec_val, if (in_len < 2) return -EINVAL; - *diag_len = 0; + cause->diag_len = 0; /* octet 3 */ - *location = lv[1] & 0x0f; - *coding = (lv[1] & 0x60) >> 5; + cause->location = lv[1] & 0x0f; + cause->coding = (lv[1] & 0x60) >> 5; i = 1; if (!(lv[i] & 0x80)) { i++; /* octet 3a */ if (in_len < i+1) return 0; - *rec = 1; - *rec_val = lv[i] & 0x7f; + cause->rec = 1; + cause->rec_val = lv[i] & 0x7f; } i++; /* octet 4 */ - *value = lv[i] & 0x7f; + cause->value = lv[i] & 0x7f; i++; if (in_len < i) /* no diag */ @@ -601,42 +712,41 @@ static int decode_cause(int *location, int *coding, int *rec, int *rec_val, return 0; /* octet 5-N */ - memcpy(diag, lv + i, in_len - (i-1)); - *diag_len = in_len - (i-1); + memcpy(cause->diag, lv + i, in_len - (i-1)); + cause->diag_len = in_len - (i-1); return 0; } /* encode 'cause' */ -static int encode_cause(int lv_only, struct msgb *msg, int location, - int coding, int rec, int rec_val, int value, - u_char *diag, u_int diag_len) +static int encode_cause(struct msgb *msg, int lv_only, + const struct gsm_mncc_cause *cause) { u_int8_t lv[32+4]; int i; - if (diag_len > 32) + if (cause->diag_len > 32) return -EINVAL; /* octet 3 */ - lv[1] = location; - lv[1] |= coding << 5; + lv[1] = cause->location; + lv[1] |= cause->coding << 5; i = 1; - if (rec) { + if (cause->rec) { i++; /* octet 3a */ - lv[i] = rec_val; + lv[i] = cause->rec_val; } lv[i] |= 0x80; /* end of octet 3 */ /* octet 4 */ i++; - lv[i] = 0x80 | value; + lv[i] = 0x80 | cause->value; /* octet 5-N */ - if (diag_len) { - memcpy(lv + i, diag, diag_len); - i += diag_len; + if (cause->diag_len) { + memcpy(lv + i, cause->diag, cause->diag_len); + i += cause->diag_len; } lv[0] = i; @@ -649,31 +759,28 @@ static int encode_cause(int lv_only, struct msgb *msg, int location, } /* encode 'calling number' */ -static int encode_calling(struct msgb *msg, int type, int plan, int present, - int screen, char *number) +static int encode_calling(struct msgb *msg, + const struct gsm_mncc_number *calling) { - return encode_callerid(msg, GSM48_IE_CALLING_BCD, type, plan, - present, screen, number); + return encode_callerid(msg, GSM48_IE_CALLING_BCD, calling); } /* encode 'connected number' */ -static int encode_connected(struct msgb *msg, int type, int plan, int present, - int screen, char *number) +static int encode_connected(struct msgb *msg, + const struct gsm_mncc_number *connected) { - return encode_callerid(msg, GSM48_IE_CONN_BCD, type, plan, - present, screen, number); + return encode_callerid(msg, GSM48_IE_CONN_BCD, connected); } /* encode 'redirecting number' */ -static int encode_redirecting(struct msgb *msg, int type, int plan, int present, - int screen, char *number) +static int encode_redirecting(struct msgb *msg, + const struct gsm_mncc_number *redirecting) { - return encode_callerid(msg, GSM48_IE_REDIR_BCD, type, plan, - present, screen, number); + return encode_callerid(msg, GSM48_IE_REDIR_BCD, redirecting); } /* decode 'facility' */ -static int decode_facility(char *info, int info_len, int *len, +static int decode_facility(struct gsm_mncc_facility *facility, const u_int8_t *lv) { u_int8_t in_len = lv[0]; @@ -681,25 +788,26 @@ static int decode_facility(char *info, int info_len, int *len, if (in_len < 1) return -EINVAL; - if (in_len > info_len) + if (in_len > sizeof(facility->info)) return -EINVAL; - memcpy(info, lv+1, in_len); - *len = in_len; + memcpy(facility->info, lv+1, in_len); + facility->len = in_len; return 0; } /* encode 'facility' */ -static int encode_facility(int lv_only, struct msgb *msg, char *info, int len) +static int encode_facility(struct msgb *msg, int lv_only, + const struct gsm_mncc_facility *facility) { u_int8_t lv[GSM_MAX_FACILITY + 1]; - if (len < 1 || len > GSM_MAX_FACILITY) + if (facility->len < 1 || facility->len > GSM_MAX_FACILITY) return -EINVAL; - memcpy(lv+1, info, len); - lv[0] = len; + memcpy(lv+1, facility->info, facility->len); + lv[0] = facility->len; if (lv_only) msgb_lv_put(msg, lv[0], lv+1); else @@ -754,7 +862,7 @@ static int encode_keypad(struct msgb *msg, int keypad) } /* decode 'progress' */ -static int decode_progress(int *coding, int *location, int *descr, +static int decode_progress(struct gsm_mncc_progress *progress, const u_int8_t *lv) { u_int8_t in_len = lv[0]; @@ -762,22 +870,22 @@ static int decode_progress(int *coding, int *location, int *descr, if (in_len < 2) return -EINVAL; - *coding = (lv[1] & 0x60) >> 5; - *location = lv[1] & 0x0f; - *descr = lv[2] & 0x7f; + progress->coding = (lv[1] & 0x60) >> 5; + progress->location = lv[1] & 0x0f; + progress->descr = lv[2] & 0x7f; return 0; } /* encode 'progress' */ -static int encode_progress(int lv_only, struct msgb *msg, int coding, - int location, int descr) +static int encode_progress(struct msgb *msg, int lv_only, + const struct gsm_mncc_progress *p) { u_int8_t lv[3]; lv[0] = 2; - lv[1] = 0x80 | ((coding & 0x3) << 5) | (location & 0xf); - lv[2] = 0x80 | (descr & 0x7f); + lv[1] = 0x80 | ((p->coding & 0x3) << 5) | (p->location & 0xf); + lv[2] = 0x80 | (p->descr & 0x7f); if (lv_only) msgb_lv_put(msg, lv[0], lv+1); else @@ -787,16 +895,18 @@ static int encode_progress(int lv_only, struct msgb *msg, int coding, } /* decode 'user-user' */ -static int decode_useruser(int *proto, char *info, int info_len, +static int decode_useruser(struct gsm_mncc_useruser *uu, const u_int8_t *lv) { u_int8_t in_len = lv[0]; + char *info = uu->info; + int info_len = sizeof(uu->info); int i; if (in_len < 1) return -EINVAL; - *proto = lv[1]; + uu->proto = lv[1]; for (i = 2; i <= in_len; i++) { info_len--; @@ -811,16 +921,17 @@ static int decode_useruser(int *proto, char *info, int info_len, } /* encode 'useruser' */ -static int encode_useruser(int lv_only, struct msgb *msg, int proto, char *info) +static int encode_useruser(struct msgb *msg, int lv_only, + const struct gsm_mncc_useruser *uu) { u_int8_t lv[GSM_MAX_USERUSER + 2]; - if (strlen(info) > GSM_MAX_USERUSER) + if (strlen(uu->info) > GSM_MAX_USERUSER) return -EINVAL; - lv[0] = 1 + strlen(info); - lv[1] = proto; - memcpy(lv + 2, info, strlen(info)); + lv[0] = 1 + strlen(uu->info); + lv[1] = uu->proto; + memcpy(lv + 2, uu->info, strlen(uu->info)); if (lv_only) msgb_lv_put(msg, lv[0], lv+1); else @@ -830,16 +941,16 @@ static int encode_useruser(int lv_only, struct msgb *msg, int proto, char *info) } /* decode 'ss version' */ -static int decode_ssversion(char *info, int info_len, int *len, +static int decode_ssversion(struct gsm_mncc_ssversion *ssv, const u_int8_t *lv) { u_int8_t in_len = lv[0]; - if (in_len < 1 || in_len < info_len) + if (in_len < 1 || in_len < sizeof(ssv->info)) return -EINVAL; - memcpy(info, lv + 1, in_len); - *len = in_len; + memcpy(ssv->info, lv + 1, in_len); + ssv->len = in_len; return 0; } @@ -862,27 +973,21 @@ struct msgb *gsm48_msgb_alloc(void) int gsm48_sendmsg(struct msgb *msg) { - struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data; - if (msg->lchan) { + struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data; msg->trx = msg->lchan->ts->trx; - if ((gh->proto_discr & GSM48_PDISC_MASK) == GSM48_PDISC_CC) { - /* Send a 04.08 call control message, add transaction - * ID and TI flag */ - gh->proto_discr |= msg->lchan->call.transaction_id; - - /* GSM 04.07 Section 11.2.3.1.3 */ - switch (msg->lchan->call.type) { - case GSM_CT_MO: - gh->proto_discr |= 0x80; - break; - case GSM_CT_MT: - break; - case GSM_CT_NONE: - break; - } - } + if ((gh->proto_discr & GSM48_PDISC_MASK) == GSM48_PDISC_CC) + DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x) " + "Sending '%s' to MS.\n", msg->trx->bts->nr, + msg->trx->nr, msg->lchan->ts->nr, + gh->proto_discr & 0xf0, + cc_msg_names[gh->msg_type & 0x3f]); + else + DEBUGP(DCC, "(bts %d trx %d ts %d pd %02x) " + "Sending 0x%02x to MS.\n", msg->trx->bts->nr, + msg->trx->nr, msg->lchan->ts->nr, + gh->proto_discr, gh->msg_type); } msg->l3h = msg->data; @@ -890,7 +995,6 @@ int gsm48_sendmsg(struct msgb *msg) return rsl_data_request(msg, 0); } - /* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */ int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause) { @@ -1080,7 +1184,7 @@ static int mm_rx_loc_upd_req(struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_loc_upd_req *lu; - struct gsm_subscriber *subscr; + struct gsm_subscriber *subscr = NULL; struct gsm_lchan *lchan = msg->lchan; u_int8_t mi_type; char mi_string[MI_SIZE]; @@ -1110,6 +1214,7 @@ static int mm_rx_loc_upd_req(struct msgb *msg) switch (mi_type) { case GSM_MI_TYPE_IMSI: + DEBUGP(DMM, "\n"); /* we always want the IMEI, too */ rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMEI); lchan->loc_operation->waiting_for_imei = 1; @@ -1118,6 +1223,7 @@ static int mm_rx_loc_upd_req(struct msgb *msg) subscr = db_create_subscriber(mi_string); break; case GSM_MI_TYPE_TMSI: + DEBUGP(DMM, "\n"); /* we always want the IMEI, too */ rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMEI); lchan->loc_operation->waiting_for_imei = 1; @@ -1140,6 +1246,12 @@ static int mm_rx_loc_upd_req(struct msgb *msg) break; } + if (!subscr) { + DEBUGP(DRR, "<- Can't find any subscriber for this ID\n"); + /* FIXME: request id? close channel? */ + return -EINVAL; + } + lchan->subscr = subscr; /* @@ -1179,6 +1291,13 @@ int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, u_int8_t mode) return gsm48_sendmsg(msg); } +#if 0 +static u_int8_t to_bcd8(u_int8_t val) +{ + return ((val / 10) << 4) | (val % 10); +} +#endif + /* Section 9.2.15a */ int gsm48_tx_mm_info(struct gsm_lchan *lchan) { @@ -1189,6 +1308,11 @@ int gsm48_tx_mm_info(struct gsm_lchan *lchan) u_int16_t *ptr16; int name_len; int i; +#if 0 + time_t cur_t; + struct tm* cur_time; + int tz15min; +#endif msg->lchan = lchan; @@ -1226,13 +1350,9 @@ int gsm48_tx_mm_info(struct gsm_lchan *lchan) } #if 0 - /* move back to the top */ - time_t cur_t; - struct tm* cur_time; - int tz15min; /* Section 10.5.3.9 */ cur_t = time(NULL); - cur_time = gmtime(cur_t); + cur_time = gmtime(&cur_t); ptr8 = msgb_put(msg, 8); ptr8[0] = GSM48_IE_NET_TIME_TZ; ptr8[1] = to_bcd8(cur_time->tm_year % 100); @@ -1243,9 +1363,9 @@ int gsm48_tx_mm_info(struct gsm_lchan *lchan) ptr8[6] = to_bcd8(cur_time->tm_sec); /* 02.42: coded as BCD encoded signed value in units of 15 minutes */ tz15min = (cur_time->tm_gmtoff)/(60*15); - ptr8[6] = to_bcd8(tz15min); + ptr8[7] = to_bcd8(tz15min); if (tz15min < 0) - ptr8[6] |= 0x80; + ptr8[7] |= 0x80; #endif return gsm48_sendmsg(msg); @@ -1353,7 +1473,7 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg) (struct gsm48_imsi_detach_ind *) gh->data; u_int8_t mi_type = idi->mi[0] & GSM_MI_TYPE_MASK; char mi_string[MI_SIZE]; - struct gsm_subscriber *subscr; + struct gsm_subscriber *subscr = NULL; mi_to_string(mi_string, sizeof(mi_string), idi->mi, idi->mi_len); DEBUGP(DMM, "IMSI DETACH INDICATION: mi_type=0x%02x MI(%s): ", @@ -1403,7 +1523,7 @@ static int gsm48_rx_mm_status(struct msgb *msg) static int gsm0408_rcv_mm(struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); - int rc; + int rc = 0; switch (gh->msg_type & 0xbf) { case GSM48_MT_MM_LOC_UPD_REQUEST: @@ -1451,7 +1571,7 @@ static int gsm48_rr_rx_pag_resp(struct msgb *msg) u_int8_t *mi_lv = gh->data + 2 + *classmark2_lv; u_int8_t mi_type = mi_lv[1] & GSM_MI_TYPE_MASK; char mi_string[MI_SIZE]; - struct gsm_subscriber *subscr; + struct gsm_subscriber *subscr = NULL; struct paging_signal_data sig_data; int rc = 0; @@ -1651,16 +1771,25 @@ int gsm48_send_rr_release(struct gsm_lchan *lchan) * ASSIGN, then first use that TCH/F for signalling and later MODE MODIFY * it for voice */ -static int gsm48_cc_tx_status(struct gsm_lchan *lchan) +static void new_cc_state(struct gsm_trans *trans, int state) +{ + if (state > 31 || state < 0) + return; + + DEBUGP(DCC, "new state %s -> %s\n", + cc_state_names[trans->state], cc_state_names[state]); + + trans->state = state; +} + +static int gsm48_cc_tx_status(struct gsm_trans *trans, void *arg) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); u_int8_t *cause, *call_state; - gh->proto_discr = GSM48_PDISC_CC; - - msg->lchan = lchan; - + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; gh->msg_type = GSM48_MT_CC_STATUS; cause = msgb_put(msg, 3); @@ -1688,125 +1817,157 @@ static int gsm48_tx_simple(struct gsm_lchan *lchan, return gsm48_sendmsg(msg); } -/* call-back from paging the B-end of the connection */ -static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *_lchan, void *param) +static void gsm48_stop_cc_timer(struct gsm_trans *trans) { - struct gsm_lchan *lchan = _lchan; - struct gsm_call *remote_call = param; - struct gsm_call *call = &lchan->call; - int rc = 0; - - if (hooknum != GSM_HOOK_RR_PAGING) - return -EINVAL; - - switch (event) { - case GSM_PAGING_SUCCEEDED: - DEBUGP(DCC, "paging succeeded!\n"); - remote_call->remote_lchan = lchan; - call->remote_lchan = remote_call->local_lchan; - /* send SETUP request to called party */ - rc = gsm48_cc_tx_setup(lchan, call->remote_lchan->subscr); - if (is_ipaccess_bts(lchan->ts->trx->bts)) - rsl_ipacc_bind(lchan); - break; - case GSM_PAGING_EXPIRED: - DEBUGP(DCC, "paging expired!\n"); - /* notify caller that we cannot reach called party */ - /* FIXME: correct cause, etc */ - rc = gsm48_tx_simple(remote_call->local_lchan, GSM48_PDISC_CC, - GSM48_MT_CC_RELEASE_COMPL); - break; + if (bsc_timer_pending(&trans->cc_timer)) { + DEBUGP(DCC, "stopping pending timer T%x\n", trans->Tcurrent); + bsc_del_timer(&trans->cc_timer); + trans->Tcurrent = 0; } - return rc; } + +static int mncc_recvmsg(struct gsm_network *net, struct gsm_trans *trans, + int msg_type, struct gsm_mncc *mncc) +{ + struct msgb *msg; + + if (trans) + if (trans->lchan) + DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x sub %s) " + "Sending '%s' to MNCC.\n", + trans->lchan->ts->trx->bts->nr, + trans->lchan->ts->trx->nr, + trans->lchan->ts->nr, trans->transaction_id, + (trans->subscr)?(trans->subscr->extension):"-", + get_mncc_name(msg_type)); + else + DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " + "Sending '%s' to MNCC.\n", + (trans->subscr)?(trans->subscr->extension):"-", + get_mncc_name(msg_type)); + else + DEBUGP(DCC, "(bts - trx - ts - ti -- sub -) " + "Sending '%s' to MNCC.\n", get_mncc_name(msg_type)); -static int gsm48_cc_rx_status_enq(struct msgb *msg) -{ - DEBUGP(DCC, "-> STATUS ENQ\n"); - return gsm48_cc_tx_status(msg->lchan); + mncc->msg_type = msg_type; + + msg = msgb_alloc(sizeof(struct gsm_mncc)); + if (!msg) + return -ENOMEM; + memcpy(msg->data, mncc, sizeof(struct gsm_mncc)); + msgb_enqueue(&net->upqueue, msg); + + return 0; } -static int gsm48_cc_rx_setup(struct msgb *msg) +int mncc_release_ind(struct gsm_network *net, struct gsm_trans *trans, + u_int32_t callref, int location, int value) { - struct gsm_call *call = &msg->lchan->call; - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct gsm_subscriber *called_subscr; - char called_number[(43-2)*2 + 1] = "\0"; - struct tlv_parsed tp; - int ret; - - if (call->state == GSM_CSTATE_NULL || - call->state == GSM_CSTATE_RELEASE_REQ) - use_lchan(msg->lchan); + struct gsm_mncc rel; - call->type = GSM_CT_MO; - call->state = GSM_CSTATE_INITIATED; - call->local_lchan = msg->lchan; - call->transaction_id = gh->proto_discr & 0xf0; - - tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0); - if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) - goto err; + memset(&rel, 0, sizeof(&rel)); + rel.callref = callref; + mncc_set_cause(&rel, 1, 1); /* Unknown subscriber */ + return mncc_recvmsg(net, trans, MNCC_REL_IND, &rel); +} - /* Parse the number that was dialed and lookup subscriber */ - decode_bcd_number(called_number, sizeof(called_number), - TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1, 1); +void free_trans(struct gsm_trans *trans) +{ + struct gsm_bts *bts; - DEBUGP(DCC, "A -> SETUP(tid=0x%02x,number='%s')\n", call->transaction_id, - called_number); + gsm48_stop_cc_timer(trans); - called_subscr = subscr_get_by_extension(called_number); - if (!called_subscr) { - DEBUGP(DCC, "could not find subscriber, RELEASE\n"); - put_lchan(msg->lchan); - return gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC, - GSM48_MT_CC_RELEASE_COMPL); - } - if (called_subscr == msg->lchan->subscr) { - DEBUGP(DCC, "subscriber calling himself ?!?\n"); - put_lchan(msg->lchan); - subscr_put(called_subscr); - return gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC, - GSM48_MT_CC_RELEASE_COMPL); + /* send release to L4, if callref still exists */ + if (trans->callref) { + /* Ressource unavailable */ + mncc_release_ind(trans->network, trans, trans->callref, 1, 47); + if (trans->state != GSM_CSTATE_NULL) + new_cc_state(trans, GSM_CSTATE_NULL); } - subscr_get(msg->lchan->subscr); - call->called_subscr = called_subscr; + if (!trans->lchan && trans->subscr && trans->subscr->net) { + /* Stop paging on all bts' */ + bts = NULL; + do { + bts = gsm_bts_by_lac(trans->subscr->net, + trans->subscr->lac, bts); + if (!bts) + break; + /* Stop paging */ + paging_request_stop(bts, trans->subscr, NULL); + } while (1); + } - /* Start paging subscriber on all BTS in LAC of subscriber */ - subscr_get_channel(called_subscr, msg->trx->bts->network, RSL_CHANNEED_TCH_F, - setup_trig_pag_evt, call); + if (trans->lchan) { + trau_mux_unmap(&trans->lchan->ts->e1_link, trans->callref); + put_lchan(trans->lchan); + } - /* send a CALL PROCEEDING message to the MO */ - ret = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC, - GSM48_MT_CC_CALL_PROC); + if (trans->subscr) + subscr_put(trans->subscr); - if (is_ipaccess_bts(msg->trx->bts)) - rsl_ipacc_bind(msg->lchan); + if (trans->state != GSM_CSTATE_NULL) + new_cc_state(trans, GSM_CSTATE_NULL); - /* change TCH/F mode to voice */ - return gsm48_tx_chan_mode_modify(msg->lchan, GSM48_CMODE_SPEECH_EFR); + llist_del(&trans->entry); -err: - /* FIXME: send some kind of RELEASE */ - return 0; + free(trans); } -static int gsm48_cc_rx_alerting(struct msgb *msg) +static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg); + +/* call-back from paging the B-end of the connection */ +static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event, + struct msgb *msg, void *_lchan, void *param) { - struct gsm_call *call = &msg->lchan->call; - - DEBUGP(DCC, "A -> ALERTING\n"); - - /* forward ALERTING to other party */ - if (!call->remote_lchan) - return -EIO; + struct gsm_lchan *lchan = _lchan; + struct gsm_subscriber *subscr = param; + struct gsm_trans *transt, *tmp; + struct gsm_network *net; + + if (hooknum != GSM_HOOK_RR_PAGING) + return -EINVAL; + + if (!subscr) + return -EINVAL; + net = subscr->net; + if (!net) { + DEBUGP(DCC, "Error Network not set!\n"); + return -EINVAL; + } - DEBUGP(DCC, "B <- ALERTING\n"); - return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC, - GSM48_MT_CC_ALERTING); + /* check all tranactions (without lchan) for subscriber */ + llist_for_each_entry_safe(transt, tmp, &net->trans_list, entry) { + if (transt->subscr != subscr || transt->lchan) + continue; + switch (event) { + case GSM_PAGING_SUCCEEDED: + if (!lchan) // paranoid + break; + DEBUGP(DCC, "Paging subscr %s succeeded!\n", + subscr->extension); + /* Assign lchan */ + if (!transt->lchan) { + transt->lchan = lchan; + use_lchan(lchan); + } + /* send SETUP request to called party */ + gsm48_cc_tx_setup(transt, &transt->cc_msg); + if (is_ipaccess_bts(lchan->ts->trx->bts)) + rsl_ipacc_bind(lchan); + break; + case GSM_PAGING_EXPIRED: + DEBUGP(DCC, "Paging subscr %s expired!\n", + subscr->extension); + /* Temporarily out of order */ + mncc_release_ind(transt->network, transt, transt->callref, + 1, 27); + transt->callref = 0; + free_trans(transt); + break; + } + } + return 0; } /* map two ipaccess RTP streams onto each other */ @@ -1847,248 +2008,1678 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan) return 0; } -static int gsm48_cc_rx_connect(struct msgb *msg) +static struct gsm_trans *get_trans_ref(struct gsm_network *net, u_int32_t callref) { - struct gsm_call *call = &msg->lchan->call; - int rc; + struct gsm_trans *trans; + llist_for_each_entry(trans, &net->trans_list, entry) { + if (trans->callref == callref) + return trans; + } + return NULL; +} - DEBUGP(DCC, "A -> CONNECT\n"); - - rc = tch_map(msg->lchan, call->remote_lchan); - if (rc) - return -EIO; - - if (!call->remote_lchan) - return -EIO; +/* bridge channels of two transactions */ +static int tch_bridge(struct gsm_network *net, u_int32_t *refs) +{ + struct gsm_trans *trans1 = get_trans_ref(net, refs[0]); + struct gsm_trans *trans2 = get_trans_ref(net, refs[1]); - DEBUGP(DCC, "A <- CONNECT ACK\n"); - /* MT+MO: need to respond with CONNECT_ACK and pass on */ - rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC, - GSM48_MT_CC_CONNECT_ACK); + if (!trans1 || !trans2) + return -EIO; + if (!trans1->lchan || !trans2->lchan) + return -EIO; - /* forward CONNECT to other party */ - DEBUGP(DCC, "B <- CONNECT\n"); - return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC, - GSM48_MT_CC_CONNECT); + /* through-connect channel */ + return tch_map(trans1->lchan, trans2->lchan); } -static int gsm48_cc_rx_disconnect(struct msgb *msg) +/* enable receive of channels to upqueue */ +static int tch_recv(struct gsm_network *net, struct gsm_mncc *data, int enable) { - struct gsm_call *call = &msg->lchan->call; - int rc; + struct gsm_trans *trans; + /* Find callref */ + trans = get_trans_ref(net, data->callref); + if (!trans) + return -EIO; + if (!trans->lchan) + return 0; - /* Section 5.4.3.2 */ - DEBUGP(DCC, "A -> DISCONNECT (state->RELEASE_REQ)\n"); - call->state = GSM_CSTATE_RELEASE_REQ; - /* FIXME: clear the network connection */ - DEBUGP(DCC, "A <- RELEASE\n"); - rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC, - GSM48_MT_CC_RELEASE); + // todo IPACCESS + if (enable) + return trau_recv_lchan(trans->lchan, data->callref); + return trau_mux_unmap(NULL, data->callref); +} - /* - * FIXME: This looks wrong! We should have a single - * place to do MMCC-REL-CNF/-REQ/-IND and then switch - * to the NULL state and relase the call - */ - subscr_put_channel(msg->lchan); +/* send a frame to channel */ +static int tch_frame(struct gsm_network *net, struct gsm_trau_frame *frame) +{ + struct gsm_trans *trans; - /* forward DISCONNECT to other party */ - if (!call->remote_lchan) + /* Find callref */ + trans = get_trans_ref(net, frame->callref); + if (!trans) return -EIO; + if (!trans->lchan) + return 0; + if (trans->lchan->type != GSM_LCHAN_TCH_F && + trans->lchan->type != GSM_LCHAN_TCH_H) + return 0; - DEBUGP(DCC, "B <- DISCONNECT\n"); - return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC, - GSM48_MT_CC_DISCONNECT); + // todo IPACCESS + return trau_send_lchan(trans->lchan, + (struct decoded_trau_frame *)frame->data); } -static const u_int8_t calling_bcd[] = { 0xb9, 0x32, 0x24 }; -int gsm48_cc_tx_setup(struct gsm_lchan *lchan, - struct gsm_subscriber *calling_subscr) +static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg) { - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - struct gsm_call *call = &lchan->call; - u_int8_t bcd_lv[19]; + DEBUGP(DCC, "-> STATUS ENQ\n"); + return gsm48_cc_tx_status(trans, msg); +} - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); +static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg); +static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg); - call->type = GSM_CT_MT; +static void gsm48_cc_timeout(void *arg) +{ + struct gsm_trans *trans = arg; + int disconnect = 0, release = 0; + int mo_cause = 102, mo_location = 0; + int l4_cause = 31, l4_location = 1; + struct gsm_mncc mo_rel, l4_rel; - call->local_lchan = msg->lchan = lchan; - use_lchan(lchan); + 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; - gh->proto_discr = GSM48_PDISC_CC; - gh->msg_type = GSM48_MT_CC_SETUP; + switch(trans->Tcurrent) { + case 0x303: + release = 1; + l4_cause = 18; + break; + case 0x310: + disconnect = 1; + l4_cause = 18; + break; + case 0x313: + disconnect = 1; + /* unknown, did not find it in the specs */ + break; + case 0x301: + disconnect = 1; + l4_cause = 19; + break; + case 0x308: + if (!trans->T308_second) { + /* restart T308 a second time */ + gsm48_cc_tx_release(trans, &trans->cc_msg); + trans->T308_second = 1; + break; /* stay in release state */ + } + free_trans(trans); + return; +// release = 1; +// l4_cause = 14; +// break; + case 0x306: + release = 1; + mo_cause = trans->cc_msg.cause.value; + mo_location = trans->cc_msg.cause.location; + break; + case 0x323: + disconnect = 1; + break; + default: + release = 1; + } + + if (release && trans->callref) { + /* process release towards layer 4 */ + mncc_release_ind(trans->network, trans, trans->callref, + l4_location, l4_cause); + trans->callref = 0; + } - msgb_tv_put(msg, GSM48_IE_SIGNAL, GSM48_SIGNAL_DIALTONE); - if (calling_subscr) { - bcd_lv[1] = 0xb9; - encode_bcd_number(bcd_lv, sizeof(bcd_lv), 1, - calling_subscr->extension); - msgb_tlv_put(msg, GSM48_IE_CALLING_BCD, - bcd_lv[0], bcd_lv+1); + if (disconnect && trans->callref) { + /* process disconnect towards layer 4 */ + mncc_set_cause(&l4_rel, l4_location, l4_cause); + mncc_recvmsg(trans->network, trans, MNCC_DISC_IND, &l4_rel); } - if (lchan->subscr) { - bcd_lv[1] = 0xb9; - encode_bcd_number(bcd_lv, sizeof(bcd_lv), 1, - lchan->subscr->extension); - msgb_tlv_put(msg, GSM48_IE_CALLED_BCD, - bcd_lv[0], bcd_lv+1); + + /* process disconnect towards mobile station */ + if (disconnect || release) { + mncc_set_cause(&mo_rel, mo_location, mo_cause); + mo_rel.cause.diag[0] = ((trans->Tcurrent & 0xf00) >> 8) + '0'; + mo_rel.cause.diag[1] = ((trans->Tcurrent & 0x0f0) >> 4) + '0'; + mo_rel.cause.diag[2] = (trans->Tcurrent & 0x00f) + '0'; + mo_rel.cause.diag_len = 3; + + if (disconnect) + gsm48_cc_tx_disconnect(trans, &mo_rel); + if (release) + gsm48_cc_tx_release(trans, &mo_rel); } - DEBUGP(DCC, "B <- SETUP\n"); +} - return gsm48_sendmsg(msg); +static void gsm48_start_cc_timer(struct gsm_trans *trans, int current, + int sec, int micro) +{ + DEBUGP(DCC, "starting timer T%x with %d seconds\n", current, sec); + trans->cc_timer.cb = gsm48_cc_timeout; + trans->cc_timer.data = trans; + bsc_schedule_timer(&trans->cc_timer, sec, micro); + trans->Tcurrent = current; } -static int gsm0408_rcv_cc(struct msgb *msg) +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; - struct gsm_call *call = &msg->lchan->call; - int rc = 0; + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc setup; - switch (msg_type) { - case GSM48_MT_CC_CALL_CONF: - /* Response to SETUP */ - DEBUGP(DCC, "-> CALL CONFIRM\n"); - /* we now need to MODIFY the channel */ - rc = gsm48_tx_chan_mode_modify(msg->lchan, GSM48_CMODE_SPEECH_EFR); - break; - case GSM48_MT_CC_RELEASE_COMPL: - /* Answer from MS to RELEASE */ - DEBUGP(DCC, "-> RELEASE COMPLETE (state->NULL)\n"); - call->state = GSM_CSTATE_NULL; - break; - case GSM48_MT_CC_ALERTING: - rc = gsm48_cc_rx_alerting(msg); - break; - case GSM48_MT_CC_CONNECT: - rc = gsm48_cc_rx_connect(msg); - break; - case GSM48_MT_CC_CONNECT_ACK: - /* MO: Answer to CONNECT */ - call->state = GSM_CSTATE_ACTIVE; - DEBUGP(DCC, "-> CONNECT_ACK (state->ACTIVE)\n"); - break; - case GSM48_MT_CC_RELEASE: - DEBUGP(DCC, "-> RELEASE\n"); - DEBUGP(DCC, "<- RELEASE_COMPLETE\n"); - /* need to respond with RELEASE_COMPLETE */ - rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC, - GSM48_MT_CC_RELEASE_COMPL); - subscr_put_channel(msg->lchan); - call->state = GSM_CSTATE_NULL; - break; - case GSM48_MT_CC_STATUS_ENQ: - rc = gsm48_cc_rx_status_enq(msg); - break; - case GSM48_MT_CC_DISCONNECT: - rc = gsm48_cc_rx_disconnect(msg); - break; - case GSM48_MT_CC_SETUP: - /* MO: wants to establish a call */ - rc = gsm48_cc_rx_setup(msg); - break; - case GSM48_MT_CC_EMERG_SETUP: - DEBUGP(DCC, "-> EMERGENCY SETUP\n"); - /* FIXME: continue with CALL_PROCEEDING, ALERTING, CONNECT, RELEASE_COMPLETE */ - break; - case GSM48_MT_CC_HOLD: - DEBUGP(DCC, "-> HOLD (rejecting)\n"); - rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC, - GSM48_MT_CC_HOLD_REJ); - break; - case GSM48_MT_CC_RETR: - DEBUGP(DCC, "-> RETR (rejecting)\n"); - rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC, - GSM48_MT_CC_RETR_REJ); - break; - default: - fprintf(stderr, "Unimplemented GSM 04.08 CC msg type 0x%02x\n", - msg_type); - break; + memset(&setup, 0, sizeof(struct gsm_mncc)); + setup.callref = trans->callref; + tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0); + /* emergency setup is identified by msg_type */ + if (msg_type == GSM48_MT_CC_EMERG_SETUP) + setup.emergency = 1; + + /* use subscriber as calling party number */ + if (trans->subscr) { + setup.fields |= MNCC_F_CALLING; + strncpy(setup.calling.number, trans->subscr->extension, + sizeof(setup.calling.number)-1); + } + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + setup.fields |= MNCC_F_BEARER_CAP; + decode_bearer_cap(&setup.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + setup.fields |= MNCC_F_FACILITY; + decode_facility(&setup.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* called party bcd number */ + if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) { + setup.fields |= MNCC_F_CALLED; + decode_called(&setup.called, + TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + setup.fields |= MNCC_F_USERUSER; + decode_useruser(&setup.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + setup.fields |= MNCC_F_SSVERSION; + decode_ssversion(&setup.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + /* CLIR suppression */ + if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_SUPP)) + setup.clir.sup = 1; + /* CLIR invocation */ + if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_INVOC)) + setup.clir.inv = 1; + /* cc cap */ + if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) { + setup.fields |= MNCC_F_CCCAP; + decode_cccap(&setup.cccap, + TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1); } - return rc; + if (is_ipaccess_bts(msg->trx->bts)) + rsl_ipacc_bind(msg->lchan); + + new_cc_state(trans, GSM_CSTATE_INITIATED); + + /* indicate setup to MNCC */ + mncc_recvmsg(trans->network, trans, MNCC_SETUP_IND, &setup); + + return 0; } -/* here we pass in a msgb from the RSL->RLL. We expect the l3 pointer to be set */ -int gsm0408_rcvmsg(struct msgb *msg) +static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg) { - struct gsm48_hdr *gh = msgb_l3(msg); - u_int8_t pdisc = gh->proto_discr & 0x0f; - int rc = 0; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + struct gsm_mncc *setup = arg; + struct gsm_trans *transt; + u_int16_t trans_id_mask = 0; + int rc, i; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + /* transaction id must not be assigned */ + if (trans->transaction_id != 0xff) { /* unasssigned */ + DEBUGP(DCC, "TX Setup with assigned transaction. " + "This is not allowed!\n"); + /* Temporarily out of order */ + rc = mncc_release_ind(trans->network, trans, trans->callref, + 1, 47); + trans->callref = 0; + free_trans(trans); + return rc; + } - switch (pdisc) { - case GSM48_PDISC_CC: - rc = gsm0408_rcv_cc(msg); - break; - case GSM48_PDISC_MM: - rc = gsm0408_rcv_mm(msg); - break; - case GSM48_PDISC_RR: - rc = gsm0408_rcv_rr(msg); - break; - case GSM48_PDISC_SMS: - rc = gsm0411_rcv_sms(msg); - break; - case GSM48_PDISC_MM_GPRS: - case GSM48_PDISC_SM_GPRS: - fprintf(stderr, "Unimplemented GSM 04.08 discriminator 0x%02d\n", - pdisc); - break; - default: - fprintf(stderr, "Unknown GSM 04.08 discriminator 0x%02d\n", - pdisc); - break; + /* Get free transaction_id */ + llist_for_each_entry(transt, &trans->network->trans_list, entry) { + /* Transaction of our lchan? */ + if (transt->lchan == trans->lchan && + transt->transaction_id != 0xff) + trans_id_mask |= (1 << (transt->transaction_id >> 4)); + } + /* Assign free transaction ID */ + if ((trans_id_mask & 0x007f) == 0x7f) { + /* no free transaction ID */ + rc = mncc_release_ind(trans->network, trans, trans->callref, + 1, 47); + trans->callref = 0; + free_trans(trans); + return rc; + } + for (i = 0; i < 7; i++) { + if ((trans_id_mask & (1 << i)) == 0) { + trans->transaction_id = i << 4; /* flag = 0 */ + break; + } } - return rc; + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_SETUP; + + gsm48_start_cc_timer(trans, 0x303, GSM48_T303); + + /* bearer capability */ + if (setup->fields & MNCC_F_BEARER_CAP) + encode_bearer_cap(msg, 0, &setup->bearer_cap); + /* facility */ + if (setup->fields & MNCC_F_FACILITY) + encode_facility(msg, 0, &setup->facility); + /* progress */ + if (setup->fields & MNCC_F_PROGRESS) + encode_progress(msg, 0, &setup->progress); + /* calling party BCD number */ + if (setup->fields & MNCC_F_CALLING) + encode_calling(msg, &setup->calling); + /* called party BCD number */ + if (setup->fields & MNCC_F_CALLED) + encode_called(msg, &setup->called); + /* user-user */ + if (setup->fields & MNCC_F_USERUSER) + encode_useruser(msg, 0, &setup->useruser); + /* redirecting party BCD number */ + if (setup->fields & MNCC_F_REDIRECTING) + encode_redirecting(msg, &setup->redirecting); + /* signal */ + if (setup->fields & MNCC_F_SIGNAL) + encode_signal(msg, setup->signal); + + new_cc_state(trans, GSM_CSTATE_CALL_PRESENT); + + return gsm48_sendmsg(msg); } -enum chreq_type { - CHREQ_T_EMERG_CALL, - CHREQ_T_CALL_REEST_TCH_F, - CHREQ_T_CALL_REEST_TCH_H, - CHREQ_T_CALL_REEST_TCH_H_DBL, - CHREQ_T_SDCCH, - CHREQ_T_TCH_F, - CHREQ_T_VOICE_CALL_TCH_H, - CHREQ_T_DATA_CALL_TCH_H, - CHREQ_T_LOCATION_UPD, - CHREQ_T_PAG_R_ANY, - CHREQ_T_PAG_R_TCH_F, - CHREQ_T_PAG_R_TCH_FH, -}; +static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc call_conf; -/* Section 9.1.8 / Table 9.9 */ -struct chreq { - u_int8_t val; - u_int8_t mask; - enum chreq_type type; -}; + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x310, GSM48_T310); -/* If SYSTEM INFORMATION TYPE 4 NECI bit == 1 */ -static const struct chreq chreq_type_neci1[] = { - { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, - { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_F }, - { 0x68, 0xfc, CHREQ_T_CALL_REEST_TCH_H }, - { 0x6c, 0xfc, CHREQ_T_CALL_REEST_TCH_H_DBL }, - { 0xe0, 0xe0, CHREQ_T_SDCCH }, - { 0x40, 0xf0, CHREQ_T_VOICE_CALL_TCH_H }, - { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, - { 0x00, 0xf0, CHREQ_T_LOCATION_UPD }, - { 0x10, 0xf0, CHREQ_T_SDCCH }, - { 0x80, 0xe0, CHREQ_T_PAG_R_ANY }, - { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, - { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, -}; + memset(&call_conf, 0, sizeof(struct gsm_mncc)); + call_conf.callref = trans->callref; + tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0); +#if 0 + /* repeat */ + if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_CIR)) + call_conf.repeat = 1; + if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_SEQ)) + call_conf.repeat = 2; +#endif + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + call_conf.fields |= MNCC_F_BEARER_CAP; + decode_bearer_cap(&call_conf.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + call_conf.fields |= MNCC_F_CAUSE; + decode_cause(&call_conf.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* cc cap */ + if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) { + call_conf.fields |= MNCC_F_CCCAP; + decode_cccap(&call_conf.cccap, + TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1); + } -/* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */ + new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF); + + return mncc_recvmsg(trans->network, trans, MNCC_CALL_CONF_IND, &call_conf); +} + +static int gsm48_cc_tx_call_proc(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *proceeding = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_CALL_PROC; + + new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC); + + /* bearer capability */ + if (proceeding->fields & MNCC_F_BEARER_CAP) + encode_bearer_cap(msg, 0, &proceeding->bearer_cap); + /* facility */ + if (proceeding->fields & MNCC_F_FACILITY) + encode_facility(msg, 0, &proceeding->facility); + /* progress */ + if (proceeding->fields & MNCC_F_PROGRESS) + encode_progress(msg, 0, &proceeding->progress); + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc alerting; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x301, GSM48_T301); + + memset(&alerting, 0, sizeof(struct gsm_mncc)); + alerting.callref = trans->callref; + tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0); + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + alerting.fields |= MNCC_F_FACILITY; + decode_facility(&alerting.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + + /* progress */ + if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { + alerting.fields |= MNCC_F_PROGRESS; + decode_progress(&alerting.progress, + TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + alerting.fields |= MNCC_F_SSVERSION; + decode_ssversion(&alerting.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED); + + return mncc_recvmsg(trans->network, trans, MNCC_ALERT_IND, &alerting); +} + +static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *alerting = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_ALERTING; + + /* facility */ + if (alerting->fields & MNCC_F_FACILITY) + encode_facility(msg, 0, &alerting->facility); + /* progress */ + if (alerting->fields & MNCC_F_PROGRESS) + encode_progress(msg, 0, &alerting->progress); + /* user-user */ + if (alerting->fields & MNCC_F_USERUSER) + encode_useruser(msg, 0, &alerting->useruser); + + new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED); + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_tx_progress(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *progress = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_PROGRESS; + + /* progress */ + encode_progress(msg, 1, &progress->progress); + /* user-user */ + if (progress->fields & MNCC_F_USERUSER) + encode_useruser(msg, 0, &progress->useruser); + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *connect = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_CONNECT; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x313, GSM48_T313); + + /* facility */ + if (connect->fields & MNCC_F_FACILITY) + encode_facility(msg, 0, &connect->facility); + /* progress */ + if (connect->fields & MNCC_F_PROGRESS) + encode_progress(msg, 0, &connect->progress); + /* connected number */ + if (connect->fields & MNCC_F_CONNECTED) + encode_connected(msg, &connect->connected); + /* user-user */ + if (connect->fields & MNCC_F_USERUSER) + encode_useruser(msg, 0, &connect->useruser); + + new_cc_state(trans, GSM_CSTATE_CONNECT_IND); + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc connect; + + gsm48_stop_cc_timer(trans); + + memset(&connect, 0, sizeof(struct gsm_mncc)); + connect.callref = trans->callref; + tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0); + /* use subscriber as connected party number */ + if (trans->subscr) { + connect.fields |= MNCC_F_CONNECTED; + strncpy(connect.connected.number, trans->subscr->extension, + sizeof(connect.connected.number)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + connect.fields |= MNCC_F_FACILITY; + decode_facility(&connect.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + connect.fields |= MNCC_F_USERUSER; + decode_useruser(&connect.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + connect.fields |= MNCC_F_SSVERSION; + decode_ssversion(&connect.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST); + + return mncc_recvmsg(trans->network, trans, MNCC_SETUP_CNF, &connect); +} + + +static int gsm48_cc_rx_connect_ack(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc connect_ack; + + gsm48_stop_cc_timer(trans); + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + memset(&connect_ack, 0, sizeof(struct gsm_mncc)); + connect_ack.callref = trans->callref; + return mncc_recvmsg(trans->network, trans, MNCC_SETUP_COMPL_IND, + &connect_ack); +} + +static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_CONNECT_ACK; + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc disc; + + gsm48_stop_cc_timer(trans); + + new_cc_state(trans, GSM_CSTATE_DISCONNECT_REQ); + + memset(&disc, 0, sizeof(struct gsm_mncc)); + disc.callref = trans->callref; + tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_CAUSE, 0); + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + disc.fields |= MNCC_F_CAUSE; + decode_cause(&disc.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + disc.fields |= MNCC_F_FACILITY; + decode_facility(&disc.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + disc.fields |= MNCC_F_USERUSER; + decode_useruser(&disc.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + disc.fields |= MNCC_F_SSVERSION; + decode_ssversion(&disc.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + return mncc_recvmsg(trans->network, trans, MNCC_DISC_IND, &disc); + +} + +static struct gsm_mncc_cause default_cause = { 1, 0, 0, 0, 31, 0, { 0 } }; + +static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *disc = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_DISCONNECT; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x306, GSM48_T306); + + /* cause */ + if (disc->fields & MNCC_F_CAUSE) + encode_cause(msg, 1, &disc->cause); + else + encode_cause(msg, 1, &default_cause); + + /* facility */ + if (disc->fields & MNCC_F_FACILITY) + encode_facility(msg, 0, &disc->facility); + /* progress */ + if (disc->fields & MNCC_F_PROGRESS) + encode_progress(msg, 0, &disc->progress); + /* user-user */ + if (disc->fields & MNCC_F_USERUSER) + encode_useruser(msg, 0, &disc->useruser); + + /* store disconnect cause for T306 expiry */ + memcpy(&trans->cc_msg, disc, sizeof(struct gsm_mncc)); + + new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND); + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc rel; + int rc; + + gsm48_stop_cc_timer(trans); + + memset(&rel, 0, sizeof(struct gsm_mncc)); + rel.callref = trans->callref; + tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0); + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + rel.fields |= MNCC_F_CAUSE; + decode_cause(&rel.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + rel.fields |= MNCC_F_FACILITY; + decode_facility(&rel.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + rel.fields |= MNCC_F_USERUSER; + decode_useruser(&rel.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + rel.fields |= MNCC_F_SSVERSION; + decode_ssversion(&rel.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + if (trans->state == GSM_CSTATE_RELEASE_REQ) { + /* release collision 5.4.5 */ + rc = mncc_recvmsg(trans->network, trans, MNCC_REL_CNF, &rel); + } else { + rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC | trans->transaction_id, + GSM48_MT_CC_RELEASE_COMPL); + rc = mncc_recvmsg(trans->network, trans, MNCC_REL_IND, &rel); + } + + new_cc_state(trans, GSM_CSTATE_NULL); + + trans->callref = 0; + free_trans(trans); + + return rc; +} + +static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *rel = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_RELEASE; + + trans->callref = 0; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x308, GSM48_T308); + + /* cause */ + if (rel->fields & MNCC_F_CAUSE) + encode_cause(msg, 0, &rel->cause); + /* facility */ + if (rel->fields & MNCC_F_FACILITY) + encode_facility(msg, 0, &rel->facility); + /* user-user */ + if (rel->fields & MNCC_F_USERUSER) + encode_useruser(msg, 0, &rel->useruser); + + trans->T308_second = 0; + memcpy(&trans->cc_msg, rel, sizeof(struct gsm_mncc)); + + if (trans->state != GSM_CSTATE_RELEASE_REQ) + new_cc_state(trans, GSM_CSTATE_RELEASE_REQ); + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc rel; + int rc = 0; + + gsm48_stop_cc_timer(trans); + + memset(&rel, 0, sizeof(struct gsm_mncc)); + rel.callref = trans->callref; + tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0); + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + rel.fields |= MNCC_F_CAUSE; + decode_cause(&rel.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + rel.fields |= MNCC_F_FACILITY; + decode_facility(&rel.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + rel.fields |= MNCC_F_USERUSER; + decode_useruser(&rel.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + rel.fields |= MNCC_F_SSVERSION; + decode_ssversion(&rel.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + if (trans->callref) { + switch (trans->state) { + case GSM_CSTATE_CALL_PRESENT: + rc = mncc_recvmsg(trans->network, trans, + MNCC_REJ_IND, &rel); + break; + case GSM_CSTATE_RELEASE_REQ: + rc = mncc_recvmsg(trans->network, trans, + MNCC_REL_CNF, &rel); + break; + default: + rc = mncc_recvmsg(trans->network, trans, + MNCC_REL_IND, &rel); + } + } + + trans->callref = 0; + free_trans(trans); + + return rc; +} + +static int gsm48_cc_tx_release_compl(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *rel = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_RELEASE_COMPL; + + trans->callref = 0; + + gsm48_stop_cc_timer(trans); + + /* cause */ + if (rel->fields & MNCC_F_CAUSE) + encode_cause(msg, 0, &rel->cause); + /* facility */ + if (rel->fields & MNCC_F_FACILITY) + encode_facility(msg, 0, &rel->facility); + /* user-user */ + if (rel->fields & MNCC_F_USERUSER) + encode_useruser(msg, 0, &rel->useruser); + + free_trans(trans); + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_rx_facility(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc fac; + + memset(&fac, 0, sizeof(struct gsm_mncc)); + fac.callref = trans->callref; + tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_FACILITY, 0); + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + fac.fields |= MNCC_F_FACILITY; + decode_facility(&fac.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + fac.fields |= MNCC_F_SSVERSION; + decode_ssversion(&fac.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + return mncc_recvmsg(trans->network, trans, MNCC_FACILITY_IND, &fac); +} + +static int gsm48_cc_tx_facility(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *fac = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_FACILITY; + + /* facility */ + encode_facility(msg, 1, &fac->facility); + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_rx_hold(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc hold; + + memset(&hold, 0, sizeof(struct gsm_mncc)); + hold.callref = trans->callref; + return mncc_recvmsg(trans->network, trans, MNCC_HOLD_IND, &hold); +} + +static int gsm48_cc_tx_hold_ack(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_HOLD_ACK; + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_tx_hold_rej(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *hold_rej = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_HOLD_REJ; + + /* cause */ + if (hold_rej->fields & MNCC_F_CAUSE) + encode_cause(msg, 1, &hold_rej->cause); + else + encode_cause(msg, 1, &default_cause); + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_rx_retrieve(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc retrieve; + + memset(&retrieve, 0, sizeof(struct gsm_mncc)); + retrieve.callref = trans->callref; + return mncc_recvmsg(trans->network, trans, MNCC_RETRIEVE_IND, &retrieve); +} + +static int gsm48_cc_tx_retrieve_ack(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_RETR_ACK; + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_tx_retrieve_rej(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *retrieve_rej = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_RETR_REJ; + + /* cause */ + if (retrieve_rej->fields & MNCC_F_CAUSE) + encode_cause(msg, 1, &retrieve_rej->cause); + else + encode_cause(msg, 1, &default_cause); + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_rx_start_dtmf(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc dtmf; + + memset(&dtmf, 0, sizeof(struct gsm_mncc)); + dtmf.callref = trans->callref; + tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0); + /* keypad facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY)) { + dtmf.fields |= MNCC_F_KEYPAD; + decode_keypad(&dtmf.keypad, + TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1); + } + + return mncc_recvmsg(trans->network, trans, MNCC_START_DTMF_IND, &dtmf); +} + +static int gsm48_cc_tx_start_dtmf_ack(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *dtmf = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_START_DTMF_ACK; + + /* keypad */ + if (dtmf->fields & MNCC_F_KEYPAD) + encode_keypad(msg, dtmf->keypad); + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_tx_start_dtmf_rej(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *dtmf = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_START_DTMF_REJ; + + /* cause */ + if (dtmf->fields & MNCC_F_CAUSE) + encode_cause(msg, 1, &dtmf->cause); + else + encode_cause(msg, 1, &default_cause); + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_tx_stop_dtmf_ack(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_STOP_DTMF_ACK; + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_rx_stop_dtmf(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc dtmf; + + memset(&dtmf, 0, sizeof(struct gsm_mncc)); + dtmf.callref = trans->callref; + + return mncc_recvmsg(trans->network, trans, MNCC_STOP_DTMF_IND, &dtmf); +} + +static int gsm48_cc_rx_modify(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc modify; + + memset(&modify, 0, sizeof(struct gsm_mncc)); + modify.callref = trans->callref; + tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + modify.fields |= MNCC_F_BEARER_CAP; + decode_bearer_cap(&modify.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + + new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY); + + return mncc_recvmsg(trans->network, trans, MNCC_MODIFY_IND, &modify); +} + +static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *modify = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_MODIFY; + + gsm48_start_cc_timer(trans, 0x323, GSM48_T323); + + /* bearer capability */ + encode_bearer_cap(msg, 1, &modify->bearer_cap); + + new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY); + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc modify; + + gsm48_stop_cc_timer(trans); + + memset(&modify, 0, sizeof(struct gsm_mncc)); + modify.callref = trans->callref; + tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + modify.fields |= MNCC_F_BEARER_CAP; + decode_bearer_cap(&modify.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return mncc_recvmsg(trans->network, trans, MNCC_MODIFY_CNF, &modify); +} + +static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *modify = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_MODIFY_COMPL; + + /* bearer capability */ + encode_bearer_cap(msg, 1, &modify->bearer_cap); + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc modify; + + gsm48_stop_cc_timer(trans); + + memset(&modify, 0, sizeof(struct gsm_mncc)); + modify.callref = trans->callref; + tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE); + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + modify.fields |= GSM48_IE_BEARER_CAP; + decode_bearer_cap(&modify.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + modify.fields |= MNCC_F_CAUSE; + decode_cause(&modify.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return mncc_recvmsg(trans->network, trans, MNCC_MODIFY_REJ, &modify); +} + +static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *modify = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_MODIFY_REJECT; + + /* bearer capability */ + encode_bearer_cap(msg, 1, &modify->bearer_cap); + /* cause */ + encode_cause(msg, 1, &modify->cause); + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *notify = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_NOTIFY; + + /* notify */ + encode_notify(msg, notify->notify); + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_rx_notify(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); +// struct tlv_parsed tp; + struct gsm_mncc notify; + + memset(¬ify, 0, sizeof(struct gsm_mncc)); + notify.callref = trans->callref; +// tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len); + if (payload_len >= 1) + decode_notify(¬ify.notify, gh->data); + + return mncc_recvmsg(trans->network, trans, MNCC_NOTIFY_IND, ¬ify); +} + +static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *user = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id; + msg->lchan = trans->lchan; + gh->msg_type = GSM48_MT_CC_USER_INFO; + + /* user-user */ + if (user->fields & MNCC_F_USERUSER) + encode_useruser(msg, 1, &user->useruser); + /* more data */ + if (user->more) + encode_more(msg); + + return gsm48_sendmsg(msg); +} + +static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc user; + + memset(&user, 0, sizeof(struct gsm_mncc)); + user.callref = trans->callref; + tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, GSM48_IE_USER_USER, 0); + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + user.fields |= MNCC_F_USERUSER; + decode_useruser(&user.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* more data */ + if (TLVP_PRESENT(&tp, GSM48_IE_MORE_DATA)) + user.more = 1; + + return mncc_recvmsg(trans->network, trans, MNCC_USERINFO_IND, &user); +} + +static int gsm48_lchan_modify(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *mode = arg; + + return gsm48_tx_chan_mode_modify(trans->lchan, mode->lchan_mode); +} + +static struct downstate { + u_int32_t states; + int type; + int (*rout) (struct gsm_trans *trans, void *arg); +} downstatelist[] = { + /* mobile originating call establishment */ + {SBIT(GSM_CSTATE_INITIATED), /* 5.2.1.2 */ + MNCC_CALL_PROC_REQ, gsm48_cc_tx_call_proc}, + {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.2 | 5.2.1.5 */ + MNCC_ALERT_REQ, gsm48_cc_tx_alerting}, + {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.2 | 5.2.1.6 | 5.2.1.6 */ + MNCC_SETUP_RSP, gsm48_cc_tx_connect}, + {SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.4.2 */ + MNCC_PROGRESS_REQ, gsm48_cc_tx_progress}, + /* mobile terminating call establishment */ + {SBIT(GSM_CSTATE_NULL), /* 5.2.2.1 */ + MNCC_SETUP_REQ, gsm48_cc_tx_setup}, + {SBIT(GSM_CSTATE_CONNECT_REQUEST), + MNCC_SETUP_COMPL_REQ, gsm48_cc_tx_connect_ack}, + /* signalling during call */ + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_NOTIFY_REQ, gsm48_cc_tx_notify}, + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), + MNCC_FACILITY_REQ, gsm48_cc_tx_facility}, + {ALL_STATES, + MNCC_START_DTMF_RSP, gsm48_cc_tx_start_dtmf_ack}, + {ALL_STATES, + MNCC_START_DTMF_REJ, gsm48_cc_tx_start_dtmf_rej}, + {ALL_STATES, + MNCC_STOP_DTMF_RSP, gsm48_cc_tx_stop_dtmf_ack}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_HOLD_CNF, gsm48_cc_tx_hold_ack}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_HOLD_REJ, gsm48_cc_tx_hold_rej}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_RETRIEVE_CNF, gsm48_cc_tx_retrieve_ack}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_RETRIEVE_REJ, gsm48_cc_tx_retrieve_rej}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_MODIFY_REQ, gsm48_cc_tx_modify}, + {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), + MNCC_MODIFY_RSP, gsm48_cc_tx_modify_complete}, + {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), + MNCC_MODIFY_REJ, gsm48_cc_tx_modify_reject}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_USERINFO_REQ, gsm48_cc_tx_userinfo}, + /* clearing */ + {SBIT(GSM_CSTATE_INITIATED), + MNCC_REJ_REQ, gsm48_cc_tx_release_compl}, + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_DISCONNECT_IND) - SBIT(GSM_CSTATE_RELEASE_REQ) - SBIT(GSM_CSTATE_DISCONNECT_REQ), /* 5.4.4 */ + MNCC_DISC_REQ, gsm48_cc_tx_disconnect}, + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */ + MNCC_REL_REQ, gsm48_cc_tx_release}, + /* special */ + {ALL_STATES, + MNCC_LCHAN_MODIFY, gsm48_lchan_modify}, +}; + +#define DOWNSLLEN \ + (sizeof(downstatelist) / sizeof(struct downstate)) + + +int mncc_send(struct gsm_network *net, int msg_type, void *arg) +{ + int i, j, k, l, rc = 0; + struct gsm_trans *trans = NULL, *transt; + struct gsm_subscriber *subscr; + struct gsm_lchan *lchan = NULL, *lchant; + struct gsm_bts *bts = NULL; + struct gsm_bts_trx *trx; + struct gsm_bts_trx_ts *ts; + struct gsm_mncc *data = arg, rel; + + /* handle special messages */ + switch(msg_type) { + case MNCC_BRIDGE: + return tch_bridge(net, arg); + case MNCC_FRAME_DROP: + return tch_recv(net, arg, 0); + case MNCC_FRAME_RECV: + return tch_recv(net, arg, 1); + case GSM_TRAU_FRAME: + return tch_frame(net, arg); + } + + memset(&rel, 0, sizeof(struct gsm_mncc)); + rel.callref = data->callref; + + /* Find callref */ + trans = get_trans_ref(net, data->callref); + + /* Callref unknown */ + if (!trans) { + if (msg_type != MNCC_SETUP_REQ || !data->called.number[0]) { + DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " + "Received '%s' from MNCC with " + "unknown callref %d\n", data->called.number, + get_mncc_name(msg_type), data->callref); + /* Invalid call reference */ + return mncc_release_ind(net, NULL, data->callref, 1, 81); + } + /* New transaction due to setup, find subscriber */ + subscr = subscr_get_by_extension(data->called.number); + /* If subscriber is not found */ + if (!subscr) { + DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " + "Received '%s' from MNCC with " + "unknown subscriber %s\n", data->called.number, + get_mncc_name(msg_type), data->called.number); + /* Unknown subscriber */ + return mncc_release_ind(net, NULL, data->callref, 1, 1); + } + /* If subscriber is not "attached" */ + if (!subscr->lac) { + DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " + "Received '%s' from MNCC with " + "detached subscriber %s\n", data->called.number, + get_mncc_name(msg_type), data->called.number); + subscr_put(subscr); + /* Temporarily out of order */ + return mncc_release_ind(net, NULL, data->callref, 1, 27); + } + /* Create transaction */ + if (!(trans = calloc(1, sizeof(struct gsm_trans)))) { + DEBUGP(DCC, "No memory for trans.\n"); + subscr_put(subscr); + /* Ressource unavailable */ + mncc_release_ind(net, NULL, data->callref, 1, 47); + return -ENOMEM; + } + trans->callref = data->callref; + trans->network = net; + trans->transaction_id = 0xff; /* unassigned */ + llist_add_tail(&trans->entry, &net->trans_list); + /* Assign subscriber to transaction */ + trans->subscr = subscr; + /* Find lchan */ + for (i = 0; i < net->num_bts; i++) { + bts = &net->bts[i]; + for (j = 0; j < bts->num_trx; j++) { + trx = &bts->trx[j]; + for (k = 0; k < TRX_NR_TS; k++) { + ts = &trx->ts[k]; + for (l = 0; l < TS_MAX_LCHAN; l++) { + lchant = &ts->lchan[l]; + if (lchant->subscr == subscr) { + lchan = lchant; + break; + } + } + } + } + } + + /* If subscriber has no lchan */ + if (!lchan) { + /* find transaction with this subscriber already paging */ + llist_for_each_entry(transt, &net->trans_list, entry) { + /* Transaction of our lchan? */ + if (transt == trans || + transt->subscr != subscr) + continue; + DEBUGP(DCC, "(bts %d trx - ts - ti -- sub %s) " + "Received '%s' from MNCC with " + "unallocated channel, paging already " + "started.\n", bts->nr, + data->called.number, + get_mncc_name(msg_type)); + return 0; + } + /* store setup informations until paging was successfull */ + memcpy(&trans->cc_msg, data, sizeof(struct gsm_mncc)); + /* start paging subscriber on all BTS with her location */ + subscr->net = net; + bts = NULL; + do { + bts = gsm_bts_by_lac(net, subscr->lac, bts); + if (!bts) + break; + DEBUGP(DCC, "(bts %d trx - ts - ti -- sub %s) " + "Received '%s' from MNCC with " + "unallocated channel, paging.\n", + bts->nr, data->called.number, + get_mncc_name(msg_type)); + /* Trigger paging */ + paging_request(bts, subscr, RSL_CHANNEED_TCH_F, + setup_trig_pag_evt, subscr); + } while (1); + return 0; + } + /* Assign lchan */ + trans->lchan = lchan; + use_lchan(lchan); + } + lchan = trans->lchan; + + /* if paging did not respond yet */ + if (!lchan) { + DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " + "Received '%s' from MNCC in paging state\n", + (trans->subscr)?(trans->subscr->extension):"-", + get_mncc_name(msg_type)); + mncc_set_cause(&rel, 1, 16); /* Normal call clearing */ + if (msg_type == MNCC_REL_REQ) + rc = mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel); + else + rc = mncc_recvmsg(net, trans, MNCC_REL_IND, &rel); + trans->callref = 0; + free_trans(trans); + return rc; + } + + DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x sub %s) " + "Received '%s' from MNCC in state %d (%s)\n", + lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, + trans->transaction_id, + (lchan->subscr)?(lchan->subscr->extension):"-", + get_mncc_name(msg_type), trans->state, + cc_state_names[trans->state]); + + /* Find function for current state and message */ + for (i = 0; i < DOWNSLLEN; i++) + if ((msg_type == downstatelist[i].type) + && ((1 << trans->state) & downstatelist[i].states)) + break; + if (i == DOWNSLLEN) { + DEBUGP(DCC, "Message unhandled at this state.\n"); + return 0; + } + + rc = downstatelist[i].rout(trans, arg); + + return rc; +} + + +static struct datastate { + u_int32_t states; + int type; + int (*rout) (struct gsm_trans *trans, struct msgb *msg); +} datastatelist[] = { + /* mobile originating call establishment */ + {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */ + GSM48_MT_CC_SETUP, gsm48_cc_rx_setup}, + {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */ + GSM48_MT_CC_EMERG_SETUP, gsm48_cc_rx_setup}, + {SBIT(GSM_CSTATE_CONNECT_IND), /* 5.2.1.2 */ + GSM48_MT_CC_CONNECT_ACK, gsm48_cc_rx_connect_ack}, + /* mobile terminating call establishment */ + {SBIT(GSM_CSTATE_CALL_PRESENT), /* 5.2.2.3.2 */ + GSM48_MT_CC_CALL_CONF, gsm48_cc_rx_call_conf}, + {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF), /* ???? | 5.2.2.3.2 */ + GSM48_MT_CC_ALERTING, gsm48_cc_rx_alerting}, + {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF) | SBIT(GSM_CSTATE_CALL_RECEIVED), /* (5.2.2.6) | 5.2.2.6 | 5.2.2.6 */ + GSM48_MT_CC_CONNECT, gsm48_cc_rx_connect}, + /* signalling during call */ + {ALL_STATES - SBIT(GSM_CSTATE_NULL), + GSM48_MT_CC_FACILITY, gsm48_cc_rx_facility}, + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_NOTIFY, gsm48_cc_rx_notify}, + {ALL_STATES, + GSM48_MT_CC_START_DTMF, gsm48_cc_rx_start_dtmf}, + {ALL_STATES, + GSM48_MT_CC_STOP_DTMF, gsm48_cc_rx_stop_dtmf}, + {ALL_STATES, + GSM48_MT_CC_STATUS_ENQ, gsm48_cc_rx_status_enq}, + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_HOLD, gsm48_cc_rx_hold}, + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_RETR, gsm48_cc_rx_retrieve}, + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_MODIFY, gsm48_cc_rx_modify}, + {SBIT(GSM_CSTATE_MO_TERM_MODIFY), + GSM48_MT_CC_MODIFY_COMPL, gsm48_cc_rx_modify_complete}, + {SBIT(GSM_CSTATE_MO_TERM_MODIFY), + GSM48_MT_CC_MODIFY_REJECT, gsm48_cc_rx_modify_reject}, + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_USER_INFO, gsm48_cc_rx_userinfo}, + /* clearing */ + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */ + GSM48_MT_CC_DISCONNECT, gsm48_cc_rx_disconnect}, + {ALL_STATES - SBIT(GSM_CSTATE_NULL), /* 5.4.4.1.2.2 */ + GSM48_MT_CC_RELEASE, gsm48_cc_rx_release}, + {ALL_STATES, /* 5.4.3.4 */ + GSM48_MT_CC_RELEASE_COMPL, gsm48_cc_rx_release_compl}, +}; + +#define DATASLLEN \ + (sizeof(datastatelist) / sizeof(struct datastate)) + +static int gsm0408_rcv_cc(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + u_int8_t msg_type = gh->msg_type & 0xbf; + u_int8_t transaction_id = (gh->proto_discr & 0xf0) ^ 0x80; /* flip */ + struct gsm_lchan *lchan = msg->lchan; + struct gsm_trans *trans = NULL, *transt; + struct gsm_network *net = lchan->ts->trx->bts->network; + int i, rc = 0; + + if (msg_type & 0x80) { + DEBUGP(DCC, "MSG 0x%2x not defined for PD error\n", msg_type); + return -EINVAL; + } + + /* Find transaction */ + llist_for_each_entry(transt, &net->trans_list, entry) { + /* Transaction of our lchan? */ + if (transt->lchan == lchan + && transt->transaction_id == transaction_id) { + trans = transt; + } + } + + DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x sub %s) " + "Received '%s' from MS in state %d (%s)\n", + lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, + transaction_id, (lchan->subscr)?(lchan->subscr->extension):"-", + cc_msg_names[msg_type], trans?(trans->state):0, + cc_state_names[trans?(trans->state):0]); + + /* Create transaction */ + if (!trans) { + DEBUGP(DCC, "Unknown transaction ID %02x, " + "creating new trans.\n", transaction_id); + /* Create transaction */ + if (!(trans = calloc(1, sizeof(struct gsm_trans)))) { + DEBUGP(DCC, "No memory for trans.\n"); + rc = gsm48_tx_simple(msg->lchan, + GSM48_PDISC_CC | transaction_id, + GSM48_MT_CC_RELEASE_COMPL); + return -ENOMEM; + } + llist_add_tail(&trans->entry, &net->trans_list); + /* Assign transaction */ + trans->callref = new_callref++; + trans->network = net; + trans->transaction_id = transaction_id; + trans->lchan = lchan; + use_lchan(lchan); + if (lchan->subscr) { + trans->subscr = lchan->subscr; + subscr_get(trans->subscr); + } + } + + /* find function for current state and message */ + for (i = 0; i < DATASLLEN; i++) + if ((msg_type == datastatelist[i].type) + && ((1 << trans->state) & datastatelist[i].states)) + break; + if (i == DATASLLEN) { + DEBUGP(DCC, "Message unhandled at this state.\n"); + return 0; + } + + rc = datastatelist[i].rout(trans, msg); + + return rc; +} + +/* here we pass in a msgb from the RSL->RLL. We expect the l3 pointer to be set */ +int gsm0408_rcvmsg(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + u_int8_t pdisc = gh->proto_discr & 0x0f; + int rc = 0; + + switch (pdisc) { + case GSM48_PDISC_CC: + rc = gsm0408_rcv_cc(msg); + break; + case GSM48_PDISC_MM: + rc = gsm0408_rcv_mm(msg); + break; + case GSM48_PDISC_RR: + rc = gsm0408_rcv_rr(msg); + break; + case GSM48_PDISC_SMS: + rc = gsm0411_rcv_sms(msg); + break; + case GSM48_PDISC_MM_GPRS: + case GSM48_PDISC_SM_GPRS: + fprintf(stderr, "Unimplemented GSM 04.08 discriminator 0x%02d\n", + pdisc); + break; + default: + fprintf(stderr, "Unknown GSM 04.08 discriminator 0x%02d\n", + pdisc); + break; + } + + return rc; +} + +/* Section 9.1.8 / Table 9.9 */ +struct chreq { + u_int8_t val; + u_int8_t mask; + enum chreq_type type; +}; + +/* If SYSTEM INFORMATION TYPE 4 NECI bit == 1 */ +static const struct chreq chreq_type_neci1[] = { + { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, + { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_F }, + { 0x68, 0xfc, CHREQ_T_CALL_REEST_TCH_H }, + { 0x6c, 0xfc, CHREQ_T_CALL_REEST_TCH_H_DBL }, + { 0xe0, 0xe0, CHREQ_T_SDCCH }, + { 0x40, 0xf0, CHREQ_T_VOICE_CALL_TCH_H }, + { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, + { 0x00, 0xf0, CHREQ_T_LOCATION_UPD }, + { 0x10, 0xf0, CHREQ_T_SDCCH }, + { 0x80, 0xe0, CHREQ_T_PAG_R_ANY }, + { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, + { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, +}; + +/* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */ static const struct chreq chreq_type_neci0[] = { { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_H }, @@ -2157,3 +3748,21 @@ enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra) fprintf(stderr, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra); return GSM_CHREQ_REASON_OTHER; } + +/* dequeue messages to layer 4 */ +int bsc_upqueue(struct gsm_network *net) +{ + struct gsm_mncc *mncc; + struct msgb *msg; + int work = 0; + + if (net) + while ((msg = msgb_dequeue(&net->upqueue))) { + mncc = (struct gsm_mncc *)msg->data; + if (net->mncc_recv) + net->mncc_recv(net, mncc->msg_type, mncc); + work = 1; /* work done */ + } + + return work; +} diff --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c index a78425f95..9ab5895ee 100644 --- a/openbsc/src/gsm_data.c +++ b/openbsc/src/gsm_data.c @@ -84,7 +84,8 @@ const char *gsm_chreq_name(enum gsm_chreq_reason_t c) } struct gsm_network *gsm_network_init(unsigned int num_bts, enum gsm_bts_type bts_type, - u_int16_t country_code, u_int16_t network_code) + u_int16_t country_code, u_int16_t network_code, + int (*mncc_recv)(struct gsm_network *, int, void *)) { int i; struct gsm_network *net; @@ -101,6 +102,11 @@ struct gsm_network *gsm_network_init(unsigned int num_bts, enum gsm_bts_type bts net->network_code = network_code; net->num_bts = num_bts; + INIT_LLIST_HEAD(&net->trans_list); + INIT_LLIST_HEAD(&net->upqueue); + + net->mncc_recv = mncc_recv; + for (i = 0; i < num_bts; i++) { struct gsm_bts *bts = &net->bts[i]; int j; @@ -118,7 +124,7 @@ struct gsm_network *gsm_network_init(unsigned int num_bts, enum gsm_bts_type bts trx->bts = bts; trx->nr = j; - for (k = 0; k < 8; k++) { + for (k = 0; k < TRX_NR_TS; k++) { struct gsm_bts_trx_ts *ts = &trx->ts[k]; int l; diff --git a/openbsc/src/ipaccess-config.c b/openbsc/src/ipaccess-config.c index b74e46e89..cc8a6c9bc 100644 --- a/openbsc/src/ipaccess-config.c +++ b/openbsc/src/ipaccess-config.c @@ -170,7 +170,7 @@ int main(int argc, char **argv) exit(2); } - gsmnet = gsm_network_init( 1, GSM_BTS_TYPE_NANOBTS_900, 1, 1); + gsmnet = gsm_network_init(1, GSM_BTS_TYPE_NANOBTS_900, 1, 1, NULL); if (!gsmnet) exit(1); diff --git a/openbsc/src/mncc.c b/openbsc/src/mncc.c new file mode 100644 index 000000000..8f93b3f68 --- /dev/null +++ b/openbsc/src/mncc.c @@ -0,0 +1,382 @@ +/* (C) 2008-2009 by Harald Welte + * (C) 2009 by Andreas Eversberg + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +#include +#include +#include +#include + +#include +#include +#include + +static struct mncc_names { + char *name; + int value; +} mncc_names[] = { + {"MNCC_SETUP_REQ", 0x0101}, + {"MNCC_SETUP_IND", 0x0102}, + {"MNCC_SETUP_RSP", 0x0103}, + {"MNCC_SETUP_CNF", 0x0104}, + {"MNCC_SETUP_COMPL_REQ",0x0105}, + {"MNCC_SETUP_COMPL_IND",0x0106}, + {"MNCC_CALL_CONF_IND", 0x0107}, + {"MNCC_CALL_PROC_REQ", 0x0108}, + {"MNCC_PROGRESS_REQ", 0x0109}, + {"MNCC_ALERT_REQ", 0x010a}, + {"MNCC_ALERT_IND", 0x010b}, + {"MNCC_NOTIFY_REQ", 0x010c}, + {"MNCC_NOTIFY_IND", 0x010d}, + {"MNCC_DISC_REQ", 0x010e}, + {"MNCC_DISC_IND", 0x010f}, + {"MNCC_REL_REQ", 0x0110}, + {"MNCC_REL_IND", 0x0111}, + {"MNCC_REL_CNF", 0x0112}, + {"MNCC_FACILITY_REQ", 0x0113}, + {"MNCC_FACILITY_IND", 0x0114}, + {"MNCC_START_DTMF_IND", 0x0115}, + {"MNCC_START_DTMF_RSP", 0x0116}, + {"MNCC_START_DTMF_REJ", 0x0117}, + {"MNCC_STOP_DTMF_IND", 0x0118}, + {"MNCC_STOP_DTMF_RSP", 0x0119}, + {"MNCC_MODIFY_REQ", 0x011a}, + {"MNCC_MODIFY_IND", 0x011b}, + {"MNCC_MODIFY_RSP", 0x011c}, + {"MNCC_MODIFY_CNF", 0x011d}, + {"MNCC_MODIFY_REJ", 0x011e}, + {"MNCC_HOLD_IND", 0x011f}, + {"MNCC_HOLD_CNF", 0x0120}, + {"MNCC_HOLD_REJ", 0x0121}, + {"MNCC_RETRIEVE_IND", 0x0122}, + {"MNCC_RETRIEVE_CNF", 0x0123}, + {"MNCC_RETRIEVE_REJ", 0x0124}, + {"MNCC_USERINFO_REQ", 0x0125}, + {"MNCC_USERINFO_IND", 0x0126}, + {"MNCC_REJ_REQ", 0x0127}, + {"MNCC_REJ_IND", 0x0128}, + + {"MNCC_BRIDGE", 0x0200}, + {"MNCC_FRAME_RECV", 0x0201}, + {"MNCC_FRAME_DROP", 0x0202}, + {"MNCC_LCHAN_MODIFY", 0x0203}, + + {"GSM_TRAU_FRAME", 0x0300}, + + {NULL, 0} +}; + +static LLIST_HEAD(call_list); + +static u_int32_t new_callref = 0x00000001; + +char *get_mncc_name(int value) +{ + int i; + + for (i = 0; mncc_names[i].name; i++) { + if (mncc_names[i].value == value) + return mncc_names[i].name; + } + + return "MNCC_Unknown"; +} + +static void free_call(struct gsm_call *call) +{ + llist_del(&call->entry); + DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref); + free(call); +} + + +struct gsm_call *get_call_ref(u_int32_t callref) +{ + struct gsm_call *callt; + + llist_for_each_entry(callt, &call_list, entry) { + if (callt->callref == callref) + return callt; + } + return NULL; +} + +void mncc_set_cause(struct gsm_mncc *data, int loc, int val) +{ + data->fields |= MNCC_F_CAUSE; + data->cause.location = loc; + data->cause.value = val; +} + +/* on incoming call, look up database and send setup to remote subscr. */ +static int mncc_setup_ind(struct gsm_call *call, int msg_type, + struct gsm_mncc *setup) +{ + struct gsm_mncc mncc; + struct gsm_call *remote; + + /* already have remote call */ + if (call->remote_ref) + return 0; + + /* create remote call */ + if (!(remote = calloc(1, sizeof(struct gsm_call)))) { + memset(&mncc, 0, sizeof(struct gsm_mncc)); + mncc.callref = call->callref; + mncc_set_cause(&mncc, 1, 47); + mncc_send(call->net, MNCC_REJ_REQ, &mncc); + free_call(call); + return 0; + } + llist_add_tail(&remote->entry, &call_list); + remote->net = call->net; + remote->callref = new_callref++; + DEBUGP(DMNCC, "(call %x) Creating new remote instance %x.\n", + call->callref, remote->callref); + + /* link remote call */ + call->remote_ref = remote->callref; + remote->remote_ref = call->callref; + + /* modify mode */ + memset(&mncc, 0, sizeof(struct gsm_mncc)); + mncc.callref = call->callref; + mncc.lchan_mode = GSM48_CMODE_SPEECH_EFR; + DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref); + mncc_send(call->net, MNCC_LCHAN_MODIFY, &mncc); + + /* send call proceeding */ + memset(&mncc, 0, sizeof(struct gsm_mncc)); + mncc.callref = call->callref; + DEBUGP(DMNCC, "(call %x) Accepting call.\n", call->callref); + mncc_send(call->net, MNCC_CALL_PROC_REQ, &mncc); + + /* send setup to remote */ +// setup->fields |= MNCC_F_SIGNAL; +// setup->signal = GSM48_SIGNAL_DIALTONE; + setup->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Forwarding SETUP to remote.\n", call->callref); + return mncc_send(remote->net, MNCC_SETUP_REQ, setup); +} + +static int mncc_alert_ind(struct gsm_call *call, int msg_type, + struct gsm_mncc *alert) +{ + struct gsm_call *remote; + + /* send alerting to remote */ + if (!(remote = get_call_ref(call->remote_ref))) + return 0; + alert->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Forwarding ALERT to remote.\n", call->callref); + return mncc_send(remote->net, MNCC_ALERT_REQ, alert); +} + +static int mncc_notify_ind(struct gsm_call *call, int msg_type, + struct gsm_mncc *notify) +{ + struct gsm_call *remote; + + /* send notify to remote */ + if (!(remote = get_call_ref(call->remote_ref))) + return 0; + notify->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Forwarding NOTIF to remote.\n", call->callref); + return mncc_send(remote->net, MNCC_NOTIFY_REQ, notify); +} + +static int mncc_setup_cnf(struct gsm_call *call, int msg_type, + struct gsm_mncc *connect) +{ + struct gsm_mncc connect_ack; + struct gsm_call *remote; + u_int32_t refs[2]; + + /* acknowledge connect */ + memset(&connect_ack, 0, sizeof(struct gsm_mncc)); + connect_ack.callref = call->callref; + DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref); + mncc_send(call->net, MNCC_SETUP_COMPL_REQ, &connect_ack); + + /* send connect message to remote */ + if (!(remote = get_call_ref(call->remote_ref))) + return 0; + connect->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Sending CONNECT to remote.\n", call->callref); + mncc_send(remote->net, MNCC_SETUP_RSP, connect); + + /* bridge tch */ + refs[0] = call->callref; + refs[1] = call->remote_ref; + DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref); + return mncc_send(call->net, MNCC_BRIDGE, refs); +} + +static int mncc_disc_ind(struct gsm_call *call, int msg_type, + struct gsm_mncc *disc) +{ + struct gsm_call *remote; + + /* send release */ + DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n", + call->callref, disc->cause.value); + mncc_send(call->net, MNCC_REL_REQ, disc); + + /* send disc to remote */ + if (!(remote = get_call_ref(call->remote_ref))) { + return 0; + } + disc->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Disconnecting remote with cause %d\n", + remote->callref, disc->cause.value); + return mncc_send(remote->net, MNCC_DISC_REQ, disc); +} + +static int mncc_rel_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *rel) +{ + struct gsm_call *remote; + + /* send release to remote */ + if (!(remote = get_call_ref(call->remote_ref))) { + free_call(call); + return 0; + } + rel->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Releasing remote with cause %d\n", + call->callref, rel->cause.value); + mncc_send(remote->net, MNCC_REL_REQ, rel); + + free_call(call); + + return 0; +} + +static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *rel) +{ + free_call(call); + return 0; +} + +int mncc_recv(struct gsm_network *net, int msg_type, void *arg) +{ + struct gsm_mncc *data = arg; + int callref; + struct gsm_call *call = NULL, *callt; + int rc = 0; + + /* Special messages */ + switch(msg_type) { + } + + /* find callref */ + callref = data->callref; + llist_for_each_entry(callt, &call_list, entry) { + if (callt->callref == callref) { + call = callt; + break; + } + } + + /* create callref, if setup is received */ + if (!call) { + if (msg_type != MNCC_SETUP_IND) + return 0; /* drop */ + /* create call */ + if (!(call = calloc(1, sizeof(struct gsm_call)))) { + struct gsm_mncc rel; + + memset(&rel, 0, sizeof(struct gsm_mncc)); + rel.callref = callref; + mncc_set_cause(&rel, 1, 47); + mncc_send(net, MNCC_REL_REQ, &rel); + return 0; + } + llist_add_tail(&call->entry, &call_list); + call->net = net; + call->callref = callref; + DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref); + } + + DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref, + get_mncc_name(msg_type)); + + switch(msg_type) { + case MNCC_SETUP_IND: + rc = mncc_setup_ind(call, msg_type, arg); + break; + case MNCC_SETUP_CNF: + rc = mncc_setup_cnf(call, msg_type, arg); + break; + case MNCC_SETUP_COMPL_IND: + break; + case MNCC_CALL_CONF_IND: + /* we now need to MODIFY the channel */ + data->lchan_mode = GSM48_CMODE_SPEECH_EFR; + mncc_send(call->net, MNCC_LCHAN_MODIFY, data); + break; + case MNCC_ALERT_IND: + rc = mncc_alert_ind(call, msg_type, arg); + break; + case MNCC_NOTIFY_IND: + rc = mncc_notify_ind(call, msg_type, arg); + break; + case MNCC_DISC_IND: + rc = mncc_disc_ind(call, msg_type, arg); + break; + case MNCC_REL_IND: + case MNCC_REJ_IND: + rc = mncc_rel_ind(call, msg_type, arg); + break; + case MNCC_REL_CNF: + rc = mncc_rel_cnf(call, msg_type, arg); + break; + case MNCC_FACILITY_IND: + break; + case MNCC_START_DTMF_IND: + break; + case MNCC_STOP_DTMF_IND: + break; + case MNCC_MODIFY_IND: + mncc_set_cause(data, 1, 79); + DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n", + call->callref, data->cause.value); + rc = mncc_send(net, MNCC_MODIFY_REJ, data); + break; + case MNCC_MODIFY_CNF: + break; + case MNCC_HOLD_IND: + mncc_set_cause(data, 1, 79); + DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n", + call->callref, data->cause.value); + rc = mncc_send(net, MNCC_HOLD_REJ, data); + break; + case MNCC_RETRIEVE_IND: + mncc_set_cause(data, 1, 79); + DEBUGP(DMNCC, "(call %x) Rejecting RETRIEVE with cause %d\n", + call->callref, data->cause.value); + rc = mncc_send(net, MNCC_RETRIEVE_REJ, data); + break; + default: + DEBUGP(DMNCC, "(call %x) Message unhandled\n", callref); + break; + } + + return rc; +} diff --git a/openbsc/src/paging.c b/openbsc/src/paging.c index 8f15e1640..53e51464a 100644 --- a/openbsc/src/paging.c +++ b/openbsc/src/paging.c @@ -221,6 +221,7 @@ static void _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr, return; } + DEBUGP(DPAG, "Start paging on bts %d.\n", bts->nr); req = (struct gsm_paging_request *)malloc(sizeof(*req)); memset(req, 0, sizeof(*req)); req->subscr = subscr_get(subscr); @@ -263,9 +264,12 @@ static void _paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *sub llist_for_each_entry_safe(req, req2, &bts_entry->pending_requests, entry) { if (req->subscr == subscr) { - if (lchan && req->cbfn) + if (lchan && req->cbfn) { + DEBUGP(DPAG, "Stop paging on bts %d, calling cbfn.\n", bts->nr); req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED, NULL, lchan, req->cbfn_param); + } else + DEBUGP(DPAG, "Stop paging on bts %d silently.\n", bts->nr); paging_remove_request(&bts->paging, req); break; } diff --git a/openbsc/src/trau_mux.c b/openbsc/src/trau_mux.c index 96f858992..196d15fa7 100644 --- a/openbsc/src/trau_mux.c +++ b/openbsc/src/trau_mux.c @@ -145,6 +145,9 @@ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss, u_int8_t trau_bits_out[TRAU_FRAME_BITS]; struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss); struct subch_mux *mx; + struct upqueue_entry *ue; + struct msgb *msg; + struct gsm_trau_frame *frame; int rc; /* decode TRAU, change it to downlink, re-encode */ @@ -152,8 +155,23 @@ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss, if (rc) return rc; - if (!dst_e1_ss) - return -EINVAL; + if (!dst_e1_ss) { + /* frame shall be sent to upqueue */ + if (!(ue = lookup_trau_upqueue(src_e1_ss))) + return -EINVAL; + if (!ue->callref) + return -EINVAL; + msg = msgb_alloc(sizeof(struct gsm_trau_frame) + sizeof(tf)); + if (!msg) + return -ENOMEM; + frame = (struct gsm_trau_frame *)msg->data; + frame->msg_type = GSM_TRAU_FRAME; + frame->callref = ue->callref; + memcpy(frame->data, &tf, sizeof(tf)); + msgb_enqueue(&ue->net->upqueue, msg); + + return 0; + } mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts); if (!mx) diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c index 44531dd50..6597e440f 100644 --- a/openbsc/src/vty_interface.c +++ b/openbsc/src/vty_interface.c @@ -326,6 +326,8 @@ static void lchan_dump_vty(struct vty *vty, struct gsm_lchan *lchan) vty_out(vty, " No Subscriber%s", VTY_NEWLINE); } +#if 0 +TODO: callref and remote callref of call must be resolved to get gsm_trans object static void call_dump_vty(struct vty *vty, struct gsm_call *call) { vty_out(vty, "Call Type %u, State %u, Transaction ID %u%s", @@ -349,6 +351,7 @@ static void call_dump_vty(struct vty *vty, struct gsm_call *call) } else vty_out(vty, "Call has no Called Subscriber%s", VTY_NEWLINE); } +#endif DEFUN(show_lchan, show_lchan_cmd, -- cgit v1.2.3 From c66b71cb76c40f88ceec12d612e48746041a38af Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 11 Jun 2009 14:23:20 +0800 Subject: use less magic numbers (04.08 CC cause values) Introduce a gsm48_cc_cause enum and use it from gsm_04_08 and MNCC, also make sure we use gsm48_cause_loc rather than plain numbers. --- openbsc/src/gsm_04_08.c | 28 ++++++++++++++++++++-------- openbsc/src/mncc.c | 15 ++++++++++----- 2 files changed, 30 insertions(+), 13 deletions(-) (limited to 'openbsc/src') diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c index 3aaa0f53c..367f7fa70 100644 --- a/openbsc/src/gsm_04_08.c +++ b/openbsc/src/gsm_04_08.c @@ -1867,7 +1867,8 @@ int mncc_release_ind(struct gsm_network *net, struct gsm_trans *trans, memset(&rel, 0, sizeof(&rel)); rel.callref = callref; - mncc_set_cause(&rel, 1, 1); /* Unknown subscriber */ + mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_UNASSIGNED_NR); return mncc_recvmsg(net, trans, MNCC_REL_IND, &rel); } @@ -2086,8 +2087,10 @@ static void gsm48_cc_timeout(void *arg) { struct gsm_trans *trans = arg; int disconnect = 0, release = 0; - int mo_cause = 102, mo_location = 0; - int l4_cause = 31, l4_location = 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_PRN_S_LU; struct gsm_mncc mo_rel, l4_rel; memset(&mo_rel, 0, sizeof(struct gsm_mncc)); @@ -2098,11 +2101,11 @@ static void gsm48_cc_timeout(void *arg) switch(trans->Tcurrent) { case 0x303: release = 1; - l4_cause = 18; + l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; break; case 0x310: disconnect = 1; - l4_cause = 18; + l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; break; case 0x313: disconnect = 1; @@ -2110,7 +2113,7 @@ static void gsm48_cc_timeout(void *arg) break; case 0x301: disconnect = 1; - l4_cause = 19; + l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; break; case 0x308: if (!trans->T308_second) { @@ -2628,7 +2631,15 @@ static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg) } -static struct gsm_mncc_cause default_cause = { 1, 0, 0, 0, 31, 0, { 0 } }; +static struct gsm_mncc_cause default_cause = { + .location = GSM48_CAUSE_LOC_PRN_S_LU, + .coding = 0, + .rec = 0, + .rec_val = 0, + .value = GSM48_CC_CAUSE_NORMAL_UNSPEC, + .diag_len = 0, + .diag = { 0 }, +}; static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg) { @@ -3463,7 +3474,8 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg) "Received '%s' from MNCC in paging state\n", (trans->subscr)?(trans->subscr->extension):"-", get_mncc_name(msg_type)); - mncc_set_cause(&rel, 1, 16); /* Normal call clearing */ + mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_NORM_CALL_CLEAR); if (msg_type == MNCC_REL_REQ) rc = mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel); else diff --git a/openbsc/src/mncc.c b/openbsc/src/mncc.c index 8f93b3f68..4282aaf3c 100644 --- a/openbsc/src/mncc.c +++ b/openbsc/src/mncc.c @@ -140,7 +140,8 @@ static int mncc_setup_ind(struct gsm_call *call, int msg_type, if (!(remote = calloc(1, sizeof(struct gsm_call)))) { memset(&mncc, 0, sizeof(struct gsm_mncc)); mncc.callref = call->callref; - mncc_set_cause(&mncc, 1, 47); + mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); mncc_send(call->net, MNCC_REJ_REQ, &mncc); free_call(call); return 0; @@ -304,7 +305,8 @@ int mncc_recv(struct gsm_network *net, int msg_type, void *arg) memset(&rel, 0, sizeof(struct gsm_mncc)); rel.callref = callref; - mncc_set_cause(&rel, 1, 47); + mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); mncc_send(net, MNCC_REL_REQ, &rel); return 0; } @@ -354,7 +356,8 @@ int mncc_recv(struct gsm_network *net, int msg_type, void *arg) case MNCC_STOP_DTMF_IND: break; case MNCC_MODIFY_IND: - mncc_set_cause(data, 1, 79); + mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL); DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n", call->callref, data->cause.value); rc = mncc_send(net, MNCC_MODIFY_REJ, data); @@ -362,13 +365,15 @@ int mncc_recv(struct gsm_network *net, int msg_type, void *arg) case MNCC_MODIFY_CNF: break; case MNCC_HOLD_IND: - mncc_set_cause(data, 1, 79); + mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL); DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n", call->callref, data->cause.value); rc = mncc_send(net, MNCC_HOLD_REJ, data); break; case MNCC_RETRIEVE_IND: - mncc_set_cause(data, 1, 79); + mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL); DEBUGP(DMNCC, "(call %x) Rejecting RETRIEVE with cause %d\n", call->callref, data->cause.value); rc = mncc_send(net, MNCC_RETRIEVE_REJ, data); -- cgit v1.2.3 From 5d4b1c2c3373c70d8b5dab09609a7f7d7ab623b1 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 12 Jun 2009 01:34:29 +0800 Subject: abis_nm: fix printing of ADMinistrative state --- openbsc/src/abis_nm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'openbsc/src') diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c index 83e6bbb48..905289b6d 100644 --- a/openbsc/src/abis_nm.c +++ b/openbsc/src/abis_nm.c @@ -694,7 +694,7 @@ static int abis_nm_rx_statechg_rep(struct msgb *mb) } if (TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) { new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); - DEBUGPC(DNM, "ADM=%02x ", nm_adm_name(new_state.administrative)); + DEBUGPC(DNM, "ADM=%02s ", nm_adm_name(new_state.administrative)); } DEBUGPC(DNM, "\n"); -- cgit v1.2.3 From a718780253cb2b76d4eb7d70dfeff3e83f5c68cc Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 12 Jun 2009 01:53:29 +0800 Subject: SMS: fix parsing of destination address BCD this bug was introduced in Andreas' MNCC patches while altering decode_bcd_number(). --- openbsc/src/gsm_04_11.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'openbsc/src') diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c index a6f3e7aac..85ff46418 100644 --- a/openbsc/src/gsm_04_11.c +++ b/openbsc/src/gsm_04_11.c @@ -190,7 +190,7 @@ static int gsm340_rx_tpdu(struct msgb *msg) /* mangle first byte to reflect length in bytes, not digits */ address_lv[0] = da_len_bytes; /* convert to real number */ - decode_bcd_number(sms->dest_addr, sizeof(sms->dest_addr), address_lv, 1); + decode_bcd_number(sms->dest_addr, sizeof(sms->dest_addr), address_lv, 0); smsp += da_len_bytes; -- cgit v1.2.3 From 92f70c577393feba97d6dcf8c1ce5e37571f85d9 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 12 Jun 2009 01:54:08 +0800 Subject: 04.08: Make mncc-harald branch work This fixes two reasons for crashes due to uninitialized memory or wrong pointer passing introduced in my own mncc modifications --- openbsc/src/gsm_04_08.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'openbsc/src') diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c index 367f7fa70..d5f12de7f 100644 --- a/openbsc/src/gsm_04_08.c +++ b/openbsc/src/gsm_04_08.c @@ -1865,7 +1865,7 @@ int mncc_release_ind(struct gsm_network *net, struct gsm_trans *trans, { struct gsm_mncc rel; - memset(&rel, 0, sizeof(&rel)); + memset(&rel, 0, sizeof(rel)); rel.callref = callref; mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_UNASSIGNED_NR); @@ -3457,7 +3457,7 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg) bts->nr, data->called.number, get_mncc_name(msg_type)); /* Trigger paging */ - paging_request(bts, subscr, RSL_CHANNEED_TCH_F, + paging_request(net, subscr, RSL_CHANNEED_TCH_F, setup_trig_pag_evt, subscr); } while (1); return 0; -- cgit v1.2.3 From 3cfdb222143d8b1d746e5b807f604afe49853f7d Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 12 Jun 2009 02:42:11 +0800 Subject: SMS: fix parsing of destination phone number --- openbsc/src/gsm_04_11.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'openbsc/src') diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c index 85ff46418..3d5820d66 100644 --- a/openbsc/src/gsm_04_11.c +++ b/openbsc/src/gsm_04_11.c @@ -186,11 +186,12 @@ static int gsm340_rx_tpdu(struct msgb *msg) rc = -EIO; goto out; } + memset(address_lv, 0, sizeof(address_lv)); memcpy(address_lv, smsp, da_len_bytes); /* mangle first byte to reflect length in bytes, not digits */ - address_lv[0] = da_len_bytes; + address_lv[0] = da_len_bytes - 1; /* convert to real number */ - decode_bcd_number(sms->dest_addr, sizeof(sms->dest_addr), address_lv, 0); + decode_bcd_number(sms->dest_addr, sizeof(sms->dest_addr), address_lv, 1); smsp += da_len_bytes; -- cgit v1.2.3 From 71aab45bbe8b34b49ac94c574e35feb562341a88 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 14 Jun 2009 22:09:12 +0800 Subject: Fix lchan refcounting in case of IMSI DETACH Removed lchan_put() after IMSI detach function. We don't need to put lchan, because we don't hold a ressource. --- openbsc/src/gsm_04_08.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'openbsc/src') diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c index d5f12de7f..05af59378 100644 --- a/openbsc/src/gsm_04_08.c +++ b/openbsc/src/gsm_04_08.c @@ -1505,8 +1505,6 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg) } else DEBUGP(DMM, "Unknown Subscriber ?!?\n"); - put_lchan(msg->lchan); - return 0; } -- cgit v1.2.3 From 7563ac97c428619f7d69b9f33ca1832127ea082d Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 14 Jun 2009 22:14:12 +0800 Subject: Use correct cause value and cause location Fixed indication of cause value and location. Replaced plain cause numbers by definitions from header file. diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c index f323a2a..39e7b1f 100644 --- a/openbsc/src/gsm_04_08.c +++ b/openbsc/src/gsm_04_08.c @@ -1865,8 +1865,7 @@ int mncc_release_ind(struct gsm_network *net, struct gsm_trans *trans, memset(&rel, 0, sizeof(rel)); rel.callref = callref; - mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_UNASSIGNED_NR); + mncc_set_cause(&rel, location, value); return mncc_recvmsg(net, trans, MNCC_REL_IND, &rel); } @@ -1879,7 +1878,9 @@ void free_trans(struct gsm_trans *trans) /* send release to L4, if callref still exists */ if (trans->callref) { /* Ressource unavailable */ - mncc_release_ind(trans->network, trans, trans->callref, 1, 47); + mncc_release_ind(trans->network, trans, trans->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); if (trans->state != GSM_CSTATE_NULL) new_cc_state(trans, GSM_CSTATE_NULL); } @@ -1960,7 +1961,7 @@ static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event, subscr->extension); /* Temporarily out of order */ mncc_release_ind(transt->network, transt, transt->callref, - 1, 27); + 1, GSM48_CC_CAUSE_DEST_OOO); transt->callref = 0; free_trans(transt); break; @@ -2270,7 +2271,8 @@ static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg) "This is not allowed!\n"); /* Temporarily out of order */ rc = mncc_release_ind(trans->network, trans, trans->callref, - 1, 47); + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); trans->callref = 0; free_trans(trans); return rc; @@ -2287,7 +2289,8 @@ static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg) if ((trans_id_mask & 0x007f) == 0x7f) { /* no free transaction ID */ rc = mncc_release_ind(trans->network, trans, trans->callref, - 1, 47); + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); trans->callref = 0; free_trans(trans); return rc; @@ -3373,14 +3376,18 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg) "unknown callref %d\n", data->called.number, get_mncc_name(msg_type), data->callref); /* Invalid call reference */ - return mncc_release_ind(net, NULL, data->callref, 1, 81); + return mncc_release_ind(net, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INVAL_TRANS_ID); } if (!data->called.number[0] && !data->called.imsi[0]) { DEBUGP(DCC, "(bts - trx - ts - ti) " "Received '%s' from MNCC with " "no number or IMSI\n", get_mncc_name(msg_type)); /* Invalid number */ - return mncc_release_ind(net, NULL, data->callref, 1, 28); + return mncc_release_ind(net, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INV_NR_FORMAT); } /* New transaction due to setup, find subscriber */ if (data->called.number[0]) @@ -3394,7 +3401,9 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg) "unknown subscriber %s\n", data->called.number, get_mncc_name(msg_type), data->called.number); /* Unknown subscriber */ - return mncc_release_ind(net, NULL, data->callref, 1, 1); + return mncc_release_ind(net, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_UNASSIGNED_NR); } /* If subscriber is not "attached" */ if (!subscr->lac) { @@ -3404,14 +3413,18 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg) get_mncc_name(msg_type), data->called.number); subscr_put(subscr); /* Temporarily out of order */ - return mncc_release_ind(net, NULL, data->callref, 1, 27); + return mncc_release_ind(net, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_DEST_OOO); } /* Create transaction */ if (!(trans = calloc(1, sizeof(struct gsm_trans)))) { DEBUGP(DCC, "No memory for trans.\n"); subscr_put(subscr); /* Ressource unavailable */ - mncc_release_ind(net, NULL, data->callref, 1, 47); + mncc_release_ind(net, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); return -ENOMEM; } trans->callref = data->callref; --- openbsc/src/gsm_04_08.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'openbsc/src') diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c index 05af59378..8e5bfe88f 100644 --- a/openbsc/src/gsm_04_08.c +++ b/openbsc/src/gsm_04_08.c @@ -1865,8 +1865,7 @@ int mncc_release_ind(struct gsm_network *net, struct gsm_trans *trans, memset(&rel, 0, sizeof(rel)); rel.callref = callref; - mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_UNASSIGNED_NR); + mncc_set_cause(&rel, location, value); return mncc_recvmsg(net, trans, MNCC_REL_IND, &rel); } @@ -1879,7 +1878,9 @@ void free_trans(struct gsm_trans *trans) /* send release to L4, if callref still exists */ if (trans->callref) { /* Ressource unavailable */ - mncc_release_ind(trans->network, trans, trans->callref, 1, 47); + mncc_release_ind(trans->network, trans, trans->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); if (trans->state != GSM_CSTATE_NULL) new_cc_state(trans, GSM_CSTATE_NULL); } @@ -1960,7 +1961,8 @@ static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event, subscr->extension); /* Temporarily out of order */ mncc_release_ind(transt->network, transt, transt->callref, - 1, 27); + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_DEST_OOO); transt->callref = 0; free_trans(transt); break; @@ -2268,7 +2270,8 @@ static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg) "This is not allowed!\n"); /* Temporarily out of order */ rc = mncc_release_ind(trans->network, trans, trans->callref, - 1, 47); + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); trans->callref = 0; free_trans(trans); return rc; @@ -2285,7 +2288,8 @@ static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg) if ((trans_id_mask & 0x007f) == 0x7f) { /* no free transaction ID */ rc = mncc_release_ind(trans->network, trans, trans->callref, - 1, 47); + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); trans->callref = 0; free_trans(trans); return rc; @@ -3369,7 +3373,9 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg) "unknown callref %d\n", data->called.number, get_mncc_name(msg_type), data->callref); /* Invalid call reference */ - return mncc_release_ind(net, NULL, data->callref, 1, 81); + return mncc_release_ind(net, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INVAL_TRANS_ID); } /* New transaction due to setup, find subscriber */ subscr = subscr_get_by_extension(data->called.number); @@ -3380,7 +3386,9 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg) "unknown subscriber %s\n", data->called.number, get_mncc_name(msg_type), data->called.number); /* Unknown subscriber */ - return mncc_release_ind(net, NULL, data->callref, 1, 1); + return mncc_release_ind(net, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_UNASSIGNED_NR); } /* If subscriber is not "attached" */ if (!subscr->lac) { @@ -3390,14 +3398,18 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg) get_mncc_name(msg_type), data->called.number); subscr_put(subscr); /* Temporarily out of order */ - return mncc_release_ind(net, NULL, data->callref, 1, 27); + return mncc_release_ind(net, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_DEST_OOO); } /* Create transaction */ if (!(trans = calloc(1, sizeof(struct gsm_trans)))) { DEBUGP(DCC, "No memory for trans.\n"); subscr_put(subscr); /* Ressource unavailable */ - mncc_release_ind(net, NULL, data->callref, 1, 47); + mncc_release_ind(net, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); return -ENOMEM; } trans->callref = data->callref; -- cgit v1.2.3