aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/gsm_04_08.c
diff options
context:
space:
mode:
Diffstat (limited to 'openbsc/src/gsm_04_08.c')
-rw-r--r--openbsc/src/gsm_04_08.c2381
1 files changed, 2006 insertions, 375 deletions
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index 90b88dfb3..8e5bfe88f 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): ",
@@ -1385,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;
}
@@ -1403,7 +1521,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 +1569,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 +1769,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 +1815,160 @@ 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, location, value);
+ 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,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ 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,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_DEST_OOO);
+ transt->callref = 0;
+ free_trans(transt);
+ break;
+ }
+ }
+ return 0;
}
/* map two ipaccess RTP streams onto each other */
@@ -1847,171 +2009,1637 @@ 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)
+/* 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]);
+
+ if (!trans1 || !trans2)
return -EIO;
-
- if (!call->remote_lchan)
+
+ if (!trans1->lchan || !trans2->lchan)
return -EIO;
- 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);
+ /* through-connect channel */
+ return tch_map(trans1->lchan, trans2->lchan);
+}
+/* enable receive of channels to upqueue */
+static int tch_recv(struct gsm_network *net, struct gsm_mncc *data, int enable)
+{
+ struct gsm_trans *trans;
+
+ /* Find callref */
+ trans = get_trans_ref(net, data->callref);
+ if (!trans)
+ return -EIO;
+ if (!trans->lchan)
+ return 0;
- /* forward CONNECT to other party */
- DEBUGP(DCC, "B <- CONNECT\n");
- return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC,
- GSM48_MT_CC_CONNECT);
+ // todo IPACCESS
+ if (enable)
+ return trau_recv_lchan(trans->lchan, data->callref);
+ return trau_mux_unmap(NULL, data->callref);
}
-static int gsm48_cc_rx_disconnect(struct msgb *msg)
+/* send a frame to channel */
+static int tch_frame(struct gsm_network *net, struct gsm_trau_frame *frame)
{
- struct gsm_call *call = &msg->lchan->call;
- int rc;
+ struct gsm_trans *trans;
+ /* 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;
- /* 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
+ return trau_send_lchan(trans->lchan,
+ (struct decoded_trau_frame *)frame->data);
+}
- /*
- * 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);
- /* forward DISCONNECT to other party */
- if (!call->remote_lchan)
- return -EIO;
+static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg)
+{
+ DEBUGP(DCC, "-> STATUS ENQ\n");
+ return gsm48_cc_tx_status(trans, msg);
+}
+
+static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg);
+static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg);
+
+static void gsm48_cc_timeout(void *arg)
+{
+ struct gsm_trans *trans = arg;
+ int disconnect = 0, release = 0;
+ int mo_cause = GSM48_CC_CAUSE_RECOVERY_TIMER;
+ int mo_location = GSM48_CAUSE_LOC_USER;
+ int l4_cause = GSM48_CC_CAUSE_NORMAL_UNSPEC;
+ int l4_location = GSM48_CAUSE_LOC_PRN_S_LU;
+ struct gsm_mncc mo_rel, l4_rel;
+
+ memset(&mo_rel, 0, sizeof(struct gsm_mncc));
+ mo_rel.callref = trans->callref;
+ memset(&l4_rel, 0, sizeof(struct gsm_mncc));
+ l4_rel.callref = trans->callref;
+
+ switch(trans->Tcurrent) {
+ case 0x303:
+ release = 1;
+ l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND;
+ break;
+ case 0x310:
+ disconnect = 1;
+ l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND;
+ break;
+ case 0x313:
+ disconnect = 1;
+ /* unknown, did not find it in the specs */
+ break;
+ case 0x301:
+ disconnect = 1;
+ l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND;
+ 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;
+ }
+
+ 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);
+ }
+
+ /* 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 <- DISCONNECT\n");
- return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC,
- GSM48_MT_CC_DISCONNECT);
}
-static const u_int8_t calling_bcd[] = { 0xb9, 0x32, 0x24 };
+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;
+}
-int gsm48_cc_tx_setup(struct gsm_lchan *lchan,
- struct gsm_subscriber *calling_subscr)
+static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ u_int8_t msg_type = gh->msg_type & 0xbf;
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc setup;
+
+ memset(&setup, 0, sizeof(struct gsm_mncc));
+ setup.callref = trans->callref;
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+ /* 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);
+ }
+
+ 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;
+}
+
+static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg)
{
struct msgb *msg = gsm48_msgb_alloc();
struct gsm48_hdr *gh;
- struct gsm_call *call = &lchan->call;
- u_int8_t bcd_lv[19];
+ 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));
- call->type = GSM_CT_MT;
-
- call->local_lchan = msg->lchan = lchan;
- use_lchan(lchan);
+ /* 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,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ trans->callref = 0;
+ free_trans(trans);
+ return rc;
+ }
+
+ /* 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,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ 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;
+ }
+ }
- gh->proto_discr = GSM48_PDISC_CC;
+ gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
+ msg->lchan = trans->lchan;
gh->msg_type = GSM48_MT_CC_SETUP;
- 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);
+ 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);
+}
+
+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;
+
+ gsm48_stop_cc_timer(trans);
+ gsm48_start_cc_timer(trans, 0x310, GSM48_T310);
+
+ 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);
+ }
+
+ 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);
}
- 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);
+ /* 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);
}
- DEBUGP(DCC, "B <- SETUP\n");
+ 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 = {
+ .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)
+{
+ 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(&notify, 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(&notify.notify, gh->data);
+
+ return mncc_recvmsg(trans->network, trans, MNCC_NOTIFY_IND, &notify);
+}
+
+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,
+ 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);
+ /* 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,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_UNASSIGNED_NR);
+ }
+ /* 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,
+ 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,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ 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(net, 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, 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
+ 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;
- struct gsm_call *call = &msg->lchan->call;
- int rc = 0;
+ 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;
- 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;
+ 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;
}
@@ -2050,21 +3678,6 @@ int gsm0408_rcvmsg(struct msgb *msg)
return rc;
}
-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,
-};
-
/* Section 9.1.8 / Table 9.9 */
struct chreq {
u_int8_t val;
@@ -2157,3 +3770,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;
+}