Radio ressources now has functions for release of radio link.

The "BA range" is used for cell re-selection.

Due to layer 1 issues, the process gets stuck very often or looses
synchronization.

After location update, I can now make a call via VTY on SDCCH:
Call control sends SETUP request after requesting an MM connection.
My phone on the network side rang!! Call control fails and requires
further debugging...
This commit is contained in:
Andreas.Eversberg 2010-06-12 16:16:54 +00:00
parent 78f6fb7713
commit 247ec7acf3
4 changed files with 291 additions and 96 deletions

View File

@ -53,6 +53,7 @@ struct gsm48_rr_hdr {
#define GSM48_RR_ST_IDLE 0
#define GSM48_RR_ST_CONN_PEND 1
#define GSM48_RR_ST_DEDICATED 2
#define GSM48_RR_ST_REL_PEND 3
/* channel description */
struct gsm48_rr_cd {
@ -96,6 +97,8 @@ struct gsm48_rrlayer {
struct llist_head downqueue;
/* timers */
struct timer_list t_rel_wait; /* wait for L2 to transmit UA */
struct timer_list t3110;
struct timer_list t3122;
struct timer_list t3124;
struct timer_list t3126;
@ -131,6 +134,10 @@ struct gsm48_rrlayer {
/* measurements */
struct gsm48_rr_meas meas;
/* BA range */
uint8_t ba_ranges;
uint32_t ba_range[16];
};
const char *get_rr_name(int value);
@ -144,5 +151,6 @@ int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc,
uint16_t *mnc, uint16_t *lac);
int gsm48_rr_enc_cm2(struct osmocom_ms *ms, struct gsm48_classmark2 *cm);
int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg);
int gsm48_rr_los(struct osmocom_ms *ms);
#endif /* _GSM48_RR_H */

View File

@ -139,8 +139,8 @@ int l23_app_init(struct osmocom_ms *ms)
{
int rc;
log_parse_category_mask(stderr_target, "DRSL:DLAPDM:DCS:DPLMN:DRR:DMM:DCC:DMNCC:DPAG:DSUM");
// log_parse_category_mask(stderr_target, "DRR:DMM:DCC:DMNCC:DPAG:DSUM");
// log_parse_category_mask(stderr_target, "DRSL:DLAPDM:DCS:DPLMN:DRR:DMM:DCC:DMNCC:DPAG:DSUM");
log_parse_category_mask(stderr_target, "DCS:DPLMN:DRR:DMM:DCC:DMNCC:DPAG:DSUM");
srand(time(NULL));

View File

@ -2254,6 +2254,8 @@ static int gsm322_cs_powerscan(struct osmocom_ms *ms)
/* start scan on radio interface */
cs->powerscan = 1;
#warning TESTING!!!!
usleep(300000);
return l1ctl_tx_pm_req_range(ms, s, e);
}
@ -2359,9 +2361,7 @@ static void gsm322_cs_loss(void *arg)
msgb_free(nmsg);
} else {
LOGP(DCS, LOGL_INFO, "Trigger RR abort.\n");
#ifdef TODO
must trigger RR abort.
#endif
gsm48_rr_los(ms);
}
}
@ -2598,25 +2598,23 @@ struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms,
static int gsm322_cs_choose(struct osmocom_ms *ms)
{
struct gsm322_cellsel *cs = &ms->cellsel;
struct gsm48_rrlayer *rr = &ms->rrlayer;
struct gsm322_ba_list *ba = NULL;
int i;
#ifdef TODO
what we have todo here:
if we return from dedicated mode and we have a ba range, we can use that for cell reselection
if (message->ranges)
ba = gsm322_cs_ba_range(ms, message->range, message->ranges);
/* NOTE: The call to this function is synchron to RR layer, so
* we may access the BA range there.
*/
if (rr->ba_ranges)
ba = gsm322_cs_ba_range(ms, rr->ba_range, rr->ba_ranges);
else {
LOGP(DCS, LOGL_INFO, "No BA range(s), try sysinfo.\n");
#endif
/* get and update BA of last received sysinfo 5* */
ba = gsm322_cs_sysinfo_sacch(ms);
if (!ba)
ba = gsm322_find_ba_list(cs, cs->sel_si.mcc,
cs->sel_si.mnc);
#ifdef TODO
}
#endif
if (!ba) {
struct msgb *nmsg;

View File

@ -1,3 +1,4 @@
#warning rr on MDL error handling (as specified in 04.08 / 04.06)
/*
* (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
*
@ -135,6 +136,58 @@ static int gsm48_decode_start_time(struct gsm48_rr_cd *cd,
return 0;
}
/* decode "BA Range" (10.5.2.1a) */
static int gsm48_decode_ba_range(const uint8_t *ba, uint8_t ba_len,
uint32_t *range, uint8_t *ranges, int max_ranges)
{
/* ba = pointer to IE without IE type and length octets
* ba_len = number of octets
* range = pointer to store decoded range
* ranges = number of ranges decoded
* max_ranges = maximum number of decoded ranges that can be stored
*/
uint16_t lower, higher;
int i, n, required_octets;
/* find out how much ba ranges will be decoded */
n = *ba++;
ba_len --;
required_octets = 5 * (n >> 1) + 3 * (n & 1);
if (required_octets > ba_len) {
LOGP(DRR, LOGL_NOTICE, "BA range IE too short: %d ranges "
"require %d octets, but only %d octets remain.\n",
n, required_octets, ba_len);
*ranges = 0;
return -EINVAL;
}
if (max_ranges > n)
LOGP(DRR, LOGL_NOTICE, "BA range %d exceed the maximum number "
"of ranges supported by this mobile (%d).\n",
n, max_ranges);
n = max_ranges;
/* decode ranges */
for (i = 0; i < n; i++) {
if (!(i & 1)) {
/* decode even range number */
lower = *ba++ << 2;
lower |= (*ba >> 6);
higher = (*ba++ & 0x3f) << 4;
higher |= *ba >> 4;
} else {
lower = (*ba++ & 0x0f) << 6;
lower |= *ba >> 2;
higher = (*ba++ & 0x03) << 8;
higher |= *ba++;
/* decode odd range number */
}
*range++ = (higher << 16) | lower;
}
*ranges = n;
return 0;
}
/*
* state transition
*/
@ -143,6 +196,7 @@ static const char *gsm48_rr_state_names[] = {
"IDLE",
"CONN PEND",
"DEDICATED",
"REL PEND",
};
static void new_rr_state(struct gsm48_rrlayer *rr, int state)
@ -184,6 +238,8 @@ static void new_rr_state(struct gsm48_rrlayer *rr, int state)
return;
gsm322_c_event(rr->ms, nmsg);
msgb_free(nmsg);
/* reset any BA range */
rr->ba_ranges = 0;
}
}
@ -321,6 +377,41 @@ int gsm48_rsl_dequeue(struct osmocom_ms *ms)
* timers handling
*/
/* special timer to ensure that UA is sent before disconnecting channel */
static void timeout_rr_t_rel_wait(void *arg)
{
struct gsm48_rrlayer *rr = arg;
LOGP(DRR, LOGL_INFO, "L2 release timer has fired, done waiting\n");
/* return to idle now */
new_rr_state(rr, GSM48_RR_ST_IDLE);
}
/* 3.4.13.1.1: Timeout of T3110 */
static void timeout_rr_t3110(void *arg)
{
struct gsm48_rrlayer *rr = arg;
struct osmocom_ms *ms = rr->ms;
struct msgb *nmsg;
uint8_t *mode;
LOGP(DRR, LOGL_INFO, "timer T3110 has fired, release locally\n");
new_rr_state(rr, GSM48_RR_ST_REL_PEND);
/* disconnect the main signalling link */
nmsg = gsm48_l3_msgb_alloc();
if (!nmsg)
return;
mode = msgb_put(nmsg, 2);
mode[0] = RSL_IE_RELEASE_MODE;
mode[1] = 1; /* local release */
gsm48_send_rsl(ms, RSL_MT_REL_REQ, nmsg);
return;
}
static void timeout_rr_t3122(void *arg)
{
LOGP(DRR, LOGL_INFO, "timer T3122 has fired\n");
@ -347,6 +438,22 @@ static void timeout_rr_t3126(void *arg)
new_rr_state(rr, GSM48_RR_ST_IDLE);
}
static void start_rr_t_rel_wait(struct gsm48_rrlayer *rr, int sec, int micro)
{
LOGP(DRR, LOGL_INFO, "starting T_rel_wait with %d seconds\n", sec);
rr->t_rel_wait.cb = timeout_rr_t_rel_wait;
rr->t_rel_wait.data = rr;
bsc_schedule_timer(&rr->t_rel_wait, sec, micro);
}
static void start_rr_t3110(struct gsm48_rrlayer *rr, int sec, int micro)
{
LOGP(DRR, LOGL_INFO, "starting T3110 with %d seconds\n", sec);
rr->t3110.cb = timeout_rr_t3110;
rr->t3110.data = rr;
bsc_schedule_timer(&rr->t3110, sec, micro);
}
static void start_rr_t3122(struct gsm48_rrlayer *rr, int sec, int micro)
{
LOGP(DRR, LOGL_INFO, "starting T3122 with %d seconds\n", sec);
@ -363,6 +470,22 @@ static void start_rr_t3126(struct gsm48_rrlayer *rr, int sec, int micro)
bsc_schedule_timer(&rr->t3126, sec, micro);
}
static void stop_rr_t_rel_wait(struct gsm48_rrlayer *rr)
{
if (bsc_timer_pending(&rr->t_rel_wait)) {
LOGP(DRR, LOGL_INFO, "stopping pending timer T_rel_wait\n");
bsc_del_timer(&rr->t_rel_wait);
}
}
static void stop_rr_t3110(struct gsm48_rrlayer *rr)
{
if (bsc_timer_pending(&rr->t3110)) {
LOGP(DRR, LOGL_INFO, "stopping pending timer T3110\n");
bsc_del_timer(&rr->t3110);
}
}
static void stop_rr_t3122(struct gsm48_rrlayer *rr)
{
if (bsc_timer_pending(&rr->t3122)) {
@ -2762,6 +2885,49 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms)
* link establishment and release
*/
/* process "Loss Of Signal" */
int gsm48_rr_los(struct osmocom_ms *ms)
{
struct gsm48_rrlayer *rr = &ms->rrlayer;
uint8_t *mode;
struct msgb *nmsg;
struct gsm48_rr_hdr *nrrh;
LOGP(DSUM, LOGL_INFO, "Radio link lost signal\n");
if (rr->state == GSM48_RR_ST_CONN_PEND) {
LOGP(DRR, LOGL_INFO, "LOS during RACH request\n");
/* stop pending RACH timer */
stop_rr_t3126(rr);
} else {
LOGP(DRR, LOGL_INFO, "LOS during dedicated mode, release "
"locally\n");
new_rr_state(rr, GSM48_RR_ST_REL_PEND);
/* release message */
nmsg = gsm48_l3_msgb_alloc();
if (!nmsg)
return -ENOMEM;
mode = msgb_put(nmsg, 2);
mode[0] = RSL_IE_RELEASE_MODE;
mode[1] = 1; /* local release */
/* start release */
return gsm48_send_rsl(ms, RSL_MT_REL_REQ, nmsg);
}
/* send inication to upper layer */
nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
if (!nmsg)
return -ENOMEM;
nrrh = (struct gsm48_rr_hdr *)nmsg->data;
nrrh->cause = RR_REL_CAUSE_UNDEFINED;
gsm48_rr_upmsg(ms, nmsg);
return 0;
}
/* activate link and send establish request */
static int gsm48_rr_dl_est(struct osmocom_ms *ms)
{
@ -2849,7 +3015,7 @@ static int gsm48_rr_estab_cnf(struct osmocom_ms *ms, struct msgb *msg)
struct msgb *nmsg;
/* if MM has releases before confirm, we start release */
if (rr->state == GSM48_RR_ST_IDLE) {
if (rr->state == GSM48_RR_ST_REL_PEND) {
LOGP(DRR, LOGL_INFO, "MM already released RR.\n");
/* release message */
nmsg = gsm48_l3_msgb_alloc();
@ -2873,29 +3039,80 @@ static int gsm48_rr_estab_cnf(struct osmocom_ms *ms, struct msgb *msg)
return gsm48_rr_upmsg(ms, nmsg);
}
/* the link is released */
static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg)
/* the link is released in pending state (by l2) */
static int gsm48_rr_rel_ind(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm48_rrlayer *rr = &ms->rrlayer;
struct msgb *nmsg;
struct gsm48_rr_hdr *nrrh;
/* deactivate channel */
LOGP(DRR, LOGL_INFO, "deactivating channel (arfcn %d)\n",
rr->cd_now.arfcn);
#ifdef TODO
release and give new arfcn
tx_ph_dm_rel_req(ms, arfcn, rr->chan_desc.chan_desc.chan_nr);
#else
l1ctl_tx_fbsb_req(ms, rr->cd_now.arfcn, 0x07, 100, 0);
#endif
LOGP(DSUM, LOGL_INFO, "Radio link is released\n");
/* do nothing, because we aleady IDLE
* or we received the rel cnf of the last connection
* while already requesting a new one (CONN PEND)
*/
/* send inication to upper layer */
nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
if (!nmsg)
return -ENOMEM;
nrrh = (struct gsm48_rr_hdr *)nmsg->data;
nrrh->cause = RR_REL_CAUSE_UNDEFINED;
gsm48_rr_upmsg(ms, nmsg);
/* start release timer, so UA will be transmitted */
start_rr_t_rel_wait(rr, 1, 500000);
/* pending release */
new_rr_state(rr, GSM48_RR_ST_REL_PEND);
return 0;
}
/* 9.1.7 CHANNEL RELEASE is received */
static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm48_rrlayer *rr = &ms->rrlayer;
struct gsm48_hdr *gh = msgb_l3(msg);
struct gsm48_chan_rel *cr = (struct gsm48_chan_rel *)gh->data;
int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cr);
struct tlv_parsed tp;
struct msgb *nmsg;
uint8_t *mode;
if (payload_len < 0) {
LOGP(DRR, LOGL_NOTICE, "Short read of CHANNEL RELEASE "
"message.\n");
return gsm48_rr_tx_rr_status(ms,
GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
}
tlv_parse(&tp, &gsm48_rr_att_tlvdef, cr->data, payload_len, 0, 0);
LOGP(DRR, LOGL_INFO, "channel release request with cause 0x%02x)\n",
cr->rr_cause);
/* BA range */
if (TLVP_PRESENT(&tp, GSM48_IE_BA_RANGE)) {
gsm48_decode_ba_range(TLVP_VAL(&tp, GSM48_IE_BA_RANGE),
*(TLVP_VAL(&tp, GSM48_IE_BA_RANGE) - 1), rr->ba_range,
&rr->ba_ranges,
sizeof(rr->ba_range) / sizeof(rr->ba_range[0]));
/* NOTE: the ranges are kept until IDLE state is returned
* (see new_rr_state)
*/
}
new_rr_state(rr, GSM48_RR_ST_REL_PEND);
/* start T3110, so that two DISCs can be sent due to T200 timeout */
start_rr_t3110(rr, 1, 500000);
/* disconnect the main signalling link */
nmsg = gsm48_l3_msgb_alloc();
if (!nmsg)
return -ENOMEM;
mode = msgb_put(nmsg, 2);
mode[0] = RSL_IE_RELEASE_MODE;
mode[1] = 0; /* normal release */
return gsm48_send_rsl(ms, RSL_MT_REL_REQ, nmsg);
}
/*
* radio ressource requests
*/
@ -2933,6 +3150,13 @@ static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg)
stop_rr_t3122(rr);
}
/* if state is not idle */
if (rr->state != GSM48_RR_ST_IDLE) {
LOGP(DRR, LOGL_INFO, "We are not IDLE yet, rejecting!\n");
cause = RR_REL_CAUSE_TRY_LATER;
goto reject;
}
/* cell selected */
if (!cs->selected) {
LOGP(DRR, LOGL_INFO, "No cell selected, rejecting!\n");
@ -3076,6 +3300,9 @@ static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg)
rc = gsm48_rr_rx_freq_redef(ms, msg);
break;
#endif
case GSM48_MT_RR_CHAN_REL:
rc = gsm48_rr_rx_chan_rel(ms, msg);
break;
default:
LOGP(DRR, LOGL_NOTICE, "Message type 0x%02x unknown.\n",
gh->msg_type);
@ -3254,6 +3481,9 @@ static int gsm48_rr_abort_req(struct osmocom_ms *ms, struct msgb *msg)
LOGP(DRR, LOGL_INFO, "Abort in dedicated state, send release "
"to layer 2.\n");
new_rr_state(rr, GSM48_RR_ST_REL_PEND);
/* release message */
nmsg = gsm48_l3_msgb_alloc();
if (!nmsg)
@ -3267,7 +3497,7 @@ static int gsm48_rr_abort_req(struct osmocom_ms *ms, struct msgb *msg)
LOGP(DRR, LOGL_INFO, "Abort in connection pending state, return to "
"idle state.\n");
/* return idle */
new_rr_state(rr, GSM48_RR_ST_IDLE);
new_rr_state(rr, GSM48_RR_ST_REL_PEND);
return 0;
}
@ -3300,8 +3530,8 @@ static int gsm48_rr_susp_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
return 0;
}
/* release confirm in dedicated mode (abort) */
static int gsm48_rr_rel_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
/* release confirm */
static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm48_rrlayer *rr = &ms->rrlayer;
struct msgb *nmsg;
@ -3309,6 +3539,9 @@ static int gsm48_rr_rel_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
LOGP(DSUM, LOGL_INFO, "Requesting channel aborted\n");
/* stop T3211 if running */
stop_rr_t3110(rr);
/* send release indication */
nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
if (!nmsg)
@ -3332,16 +3565,23 @@ static struct dldatastate {
int type;
int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
} dldatastatelist[] = {
/* data transfer */
{SBIT(GSM48_RR_ST_IDLE) |
SBIT(GSM48_RR_ST_CONN_PEND) |
SBIT(GSM48_RR_ST_DEDICATED),
SBIT(GSM48_RR_ST_DEDICATED) |
SBIT(GSM48_RR_ST_REL_PEND),
RSL_MT_UNIT_DATA_IND, gsm48_rr_unit_data_ind},
{SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.2 */
RSL_MT_DATA_IND, gsm48_rr_data_ind},
/* esablish */
{SBIT(GSM48_RR_ST_CONN_PEND), /* 3.3.1.1.2 */
RSL_MT_CHAN_CNF, gsm48_rr_tx_rand_acc},
{SBIT(GSM48_RR_ST_IDLE) |
SBIT(GSM48_RR_ST_CONN_PEND),
SBIT(GSM48_RR_ST_CONN_PEND) |
SBIT(GSM48_RR_ST_REL_PEND),
RSL_MT_EST_CONF, gsm48_rr_estab_cnf},
#if 0
@ -3351,23 +3591,19 @@ static struct dldatastate {
{SBIT(GSM_RRSTATE),
RSL_MT_CONNECT_CNF, gsm48_rr_connect_cnf},
{SBIT(GSM_RRSTATE),
RSL_MT_RELEASE_IND, gsm48_rr_rel_ind},
#endif
{SBIT(GSM48_RR_ST_IDLE) |
SBIT(GSM48_RR_ST_CONN_PEND),
/* release */
{SBIT(GSM48_RR_ST_DEDICATED),
RSL_MT_REL_IND, gsm48_rr_rel_ind},
{SBIT(GSM48_RR_ST_REL_PEND),
RSL_MT_REL_CONF, gsm48_rr_rel_cnf},
{SBIT(GSM48_RR_ST_DEDICATED),
RSL_MT_REL_CONF, gsm48_rr_rel_cnf_dedicated},
/* suspenion */
{SBIT(GSM48_RR_ST_DEDICATED),
RSL_MT_SUSP_CONF, gsm48_rr_susp_cnf_dedicated},
{SBIT(GSM48_RR_ST_CONN_PEND), /* 3.3.1.1.2 */
RSL_MT_CHAN_CNF, gsm48_rr_tx_rand_acc},
#if 0
{SBIT(GSM48_RR_ST_DEDICATED),
RSL_MT_CHAN_CNF, gsm48_rr_rand_acc_cnf_dedicated},
@ -3420,7 +3656,8 @@ static struct rrdownstate {
int type;
int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
} rrdownstatelist[] = {
{SBIT(GSM48_RR_ST_IDLE), /* 3.3.1.1 */
/* NOTE: If not IDLE, it is rejected there. */
{ALL_STATES, /* 3.3.1.1 */
GSM48_RR_EST_REQ, gsm48_rr_est_req},
{SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.2 */
@ -3511,6 +3748,8 @@ int gsm48_rr_exit(struct osmocom_ms *ms)
rr->rr_est_msg = NULL;
}
stop_rr_t_rel_wait(rr);
stop_rr_t3110(rr);
stop_rr_t3122(rr);
stop_rr_t3126(rr);
@ -3676,52 +3915,6 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
return 0;
}
/* decode "BA Range" (10.5.2.1a) */
static int gsm48_decode_ba_range(uint8_t *ba, uint8_t, ba_len, uint32_t *range,
uint8_t *ranges, int max_ranges)
{
/* ba = pointer to IE without IE type and length octets
* ba_len = number of octets
* range = pointer to store decoded range
* ranges = number of ranges decoded
* max_ranges = maximum number of decoded ranges that can be stored
*/
uint16_t lower, higher;
int i, n, required_octets;
/* find out how much ba ranges will be decoded */
n = *ba++;
ba_len --;
required_octets = 5 * (n >> 1) + 3 * (n & 1);
if (required_octets > n) {
*ranges = 0;
return -EINVAL;
}
if (max_ranges > n)
n = max_ranges;
/* decode ranges */
for (i = 0; i < n; i++) {
if (!(i & 1)) {
/* decode even range number */
lower = *ba++ << 2;
lower |= (*ba >> 6);
higher = (*ba++ & 0x3f) << 4;
higher |= *ba >> 4;
} else {
lower = (*ba++ & 0x0f) << 6;
lower |= *ba >> 2;
higher = (*ba++ & 0x03) << 8;
higher |= *ba++;
/* decode odd range number */
}
*range++ = (higher << 16) | lower;
}
*ranges = n;
return 0;
}
/* decode "Cell Description" (10.5.2.2) */
static int gsm48_decode_cell_desc(struct gsm48_cell_desc *cd, uint16_t *arfcn, uint8_t *ncc uint8_t *bcc)
@ -3827,10 +4020,6 @@ static int gsm48_rr_connect_cnf(struct osmocom_ms *ms, struct msgbl *msg)
{
}
static int gsm48_rr_rel_ind(struct osmocom_ms *ms, struct msgb *msg)
{
}
static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm48_rrlayer *rr = ms->rrlayer;
@ -3898,7 +4087,7 @@ static void timeout_rr_t3124(void *arg)
nmsg = gsm48_l3_msgb_alloc();
if (!nmsg)
return -ENOMEM;
return gsm48_send_rsl(ms, RSL_MT_EST_REQ, nmsg);
return gsm48_send_rsl(ms, RSL_MT_REEST_REQ, nmsg);
todo
}