diff options
Diffstat (limited to 'src/host/layer23/src/mobile/gsm48_rr.c')
-rw-r--r-- | src/host/layer23/src/mobile/gsm48_rr.c | 2230 |
1 files changed, 1849 insertions, 381 deletions
diff --git a/src/host/layer23/src/mobile/gsm48_rr.c b/src/host/layer23/src/mobile/gsm48_rr.c index a94fc99c..83287c14 100644 --- a/src/host/layer23/src/mobile/gsm48_rr.c +++ b/src/host/layer23/src/mobile/gsm48_rr.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /* Very short description of some of the procedures: @@ -38,9 +34,56 @@ * When the assignment or handover fails, the old channel is activate and the * link is established again. Also pending messages are sent. * + * Group Channel: + * + * A group receive/transmit mode is not documented at GSM 04.07. The group + * receive mode is similar to the IDLE mode, except that the MS is listening + * to a TCH. The group transmit mode is similar to the DEDICATED mode. Special + * (undocumented) commands are used to enter group receive and transmit mode. + * + * There is a substate that indicates group receive/transmit mode. If the + * substate is set to group receive, the IDLE mode becomes the group receive + * mode. If the substate is set to group transmit, the dedicated mode becomes + * the group transmit mode. The substate set to group receive allows to enter + * regular dedicated mode and return back to group receive mode afterwards. + * + * new_rr_state(rr, GSM48_RR_ST_IDLE) is used to return to IDLE mode or to + * group receive mode, depending on the substate. + * + * The Uplink access on group channel: + * + * If the uplink is busy, wait until it becomes free (Uplink investigation + * procedure). Abort, if the procedure times out, if the VGCS UPLINK GRANT + * message is recived for a different talker. + * + * Request uplink using access bursts on TCH until an VGCS UPLINK GRANT is + * received. Abort, if it the procedure times out, if the uplink becomes busy, + * if the VGCS UPLINK GRANT message references a different frame numer. + * + * Establish layer 2 with TALKER INDICATION. Abort, if content resolution + * mismatches (RR_REL_IND), if link fails (MDL_ERROR), if uplink becomes free. + * + * Release uplink and wait until uplink becomes free. Abort, if UPLINK RELEASE + * is received or if uplink fails. + * + * New primitives are invented for group/broadcast calls. They are not + * specified in any recommendation. They are: + * + * GSM48_MM_EVENT_NOTIFICATION: Notify MM layer about received/ceased call. + * GSM48_MM_EVENT_UPLINK_BUSY: Notify MM layer about uplink becoming busy. + * GSM48_MM_EVENT_UPLINK_FREE: Notify MM layer about uplink becoming free. + * + * RR_GROUP_REQ: The MM layer requests group channel in receive mode. + * RR_GROUP_CNF: The RR confirms group channel. + * RR_GROUP_REL_REQ: The MM layer releases group channel. + * RR_GROUP_REL_IND: The RR indicates/confirms release of group channel. + * RR_UPLINK_REQ: The MM layer requests uplink (group transmit mode). + * RR_UPLINK_CNF: The RR layer confirms uplink. (Uplink was granted.) + * RR_UPLINK_REL_REQ: The MM layer requests release of uplink. + * RR_UPLINK_REL_IND: The RR layer indicates/confirms release of uplink */ -/* Testing delayed (immediate) assigment / handover +/* Testing delayed (immediate) assignment / handover * * When enabled, the starting time will be set by given frames in the future. * If a starting time is given by the network, this time is ignored. @@ -73,32 +116,49 @@ #include <osmocom/core/bitvec.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/l1l2_interface.h> #include <osmocom/bb/common/l23_app.h> #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/networks.h> #include <osmocom/bb/common/l1ctl.h> -#include <osmocom/bb/mobile/vty.h> #include <osmocom/bb/common/utils.h> +#include <osmocom/bb/common/settings.h> + +#include <osmocom/bb/mobile/vty.h> +#include <osmocom/bb/mobile/gsm48_rr.h> #include <l1ctl_proto.h> +/* Check response for the last 3 channel requests only. See TS 44.018 §3.3.1.1.3.1 and §3.3.1.1.3.2. */ +#define IMM_ASS_HISTORY 3 +/* Check response for up to 5 uplink requests. See TS 44.018 §3.3.1.2.1.2. */ +#define UL_GRANT_HISTROY 5 + +static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t *ma_len); +static int gsm48_rr_activate_channel(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t ma_len); static void start_rr_t_meas(struct gsm48_rrlayer *rr, int sec, int micro); static void stop_rr_t_starting(struct gsm48_rrlayer *rr); static void stop_rr_t3124(struct gsm48_rrlayer *rr); static int gsm48_rcv_rsl(struct osmocom_ms *ms, struct msgb *msg); static int gsm48_rr_dl_est(struct osmocom_ms *ms); static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms); -static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr, - uint8_t mode); +static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr, uint8_t mode, uint8_t tch_flags); static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg); +int gsm414_rcv_test(struct osmocom_ms *ms, const struct msgb *msg); +static int gsm48_rr_group_rel(struct osmocom_ms *ms, int cause); +static int gsm48_rr_uplink_access(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_rr_uplink_access_abort(struct osmocom_ms *ms, uint8_t cause); +static int gsm48_rr_uplink_status(struct osmocom_ms *ms, uint32_t event); +static int gsm48_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *ref, uint8_t hist_num); +static int gsm48_rr_tx_talker_indication(struct osmocom_ms *ms); +static int gsm48_rr_tx_rand_acc_dedicated(struct osmocom_ms *ms, uint8_t ref, uint16_t offset, int8_t uic); +static int gsm48_rr_uplink_rel_req(struct osmocom_ms *ms, struct msgb *msg); /* * support */ -#define MIN(a, b) ((a < b) ? a : b) - /* decode "Power Command" (10.5.2.28) and (10.5.2.28a) */ static int gsm48_decode_power_cmd_acc(struct gsm48_power_cmd *pc, uint8_t *power_level, uint8_t *atc) @@ -210,7 +270,7 @@ static int gsm48_apply_v_sd(struct gsm48_rrlayer *rr, struct msgb *msg) case GSM48_PDISC_MM: case GSM48_PDISC_CC: case GSM48_PDISC_NC_SS: - /* all thre pdiscs share the same V(SD) */ + /* all three pdiscs share the same V(SD) */ pdisc = GSM48_PDISC_MM; // fall through case GSM48_PDISC_GROUP_CC: @@ -249,8 +309,14 @@ static uint8_t gsm48_rr_check_mode(struct osmocom_ms *ms, uint8_t chan_nr, struct gsm_settings *set = &ms->settings; uint8_t ch_type, ch_subch, ch_ts; + if (rsl_dec_chan_nr(chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, chan_nr); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } + /* only complain if we use TCH/F or TCH/H */ - rsl_dec_chan_nr(chan_nr, &ch_type, &ch_subch, &ch_ts); if (ch_type != RSL_CHAN_Bm_ACCHs && ch_type != RSL_CHAN_Lm_ACCHs) return 0; @@ -307,6 +373,56 @@ static uint8_t gsm48_rr_check_mode(struct osmocom_ms *ms, uint8_t chan_nr, LOGP(DRR, LOGL_INFO, "Mode: half-rate speech V3\n"); } break; + case GSM48_CMODE_DATA_14k5: + if (ch_type != RSL_CHAN_Bm_ACCHs) { + LOGP(DRR, LOGL_ERROR, "TCH/F is expected for mode %s\n", + gsm48_chan_mode_name(mode)); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } else if (!set->csd_tch_f144) { + LOGP(DRR, LOGL_ERROR, "Not supporting TCH/F14.4 data (%s)\n", + gsm48_chan_mode_name(mode)); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } + LOGP(DRR, LOGL_INFO, "Mode: TCH/F14.4 data (%s)\n", + gsm48_chan_mode_name(mode)); + break; + case GSM48_CMODE_DATA_12k0: + if (ch_type != RSL_CHAN_Bm_ACCHs) { + LOGP(DRR, LOGL_ERROR, "TCH/F is expected for mode %s\n", + gsm48_chan_mode_name(mode)); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } else if (!set->csd_tch_f96) { + LOGP(DRR, LOGL_ERROR, "Not supporting TCH/F9.6 data (%s)\n", + gsm48_chan_mode_name(mode)); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } + LOGP(DRR, LOGL_INFO, "Mode: TCH/F9.6 data (%s)\n", + gsm48_chan_mode_name(mode)); + break; + case GSM48_CMODE_DATA_6k0: + if ((ch_type == RSL_CHAN_Bm_ACCHs && !set->csd_tch_f48) + || (ch_type == RSL_CHAN_Lm_ACCHs && !set->csd_tch_h48)) { + LOGP(DRR, LOGL_ERROR, "Not supporting TCH/%c4.8 data (%s)\n", + ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H', + gsm48_chan_mode_name(mode)); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } + LOGP(DRR, LOGL_INFO, "Mode: TCH/%c4.8 data (%s)\n", + ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H', + gsm48_chan_mode_name(mode)); + break; + case GSM48_CMODE_DATA_3k6: + if ((ch_type == RSL_CHAN_Bm_ACCHs && !set->csd_tch_f24) + || (ch_type == RSL_CHAN_Lm_ACCHs && !set->csd_tch_h24)) { + LOGP(DRR, LOGL_ERROR, "Not supporting TCH/%c2.4 data (%s)\n", + ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H', + gsm48_chan_mode_name(mode)); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } + LOGP(DRR, LOGL_INFO, "Mode: TCH/%c2.4 data (%s)\n", + ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H', + gsm48_chan_mode_name(mode)); + break; default: LOGP(DRR, LOGL_ERROR, "Mode 0x%02x not supported!\n", mode); return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; @@ -319,7 +435,7 @@ static uint8_t gsm48_rr_check_mode(struct osmocom_ms *ms, uint8_t chan_nr, int gsm48_rr_alter_delay(struct osmocom_ms *ms) { struct gsm48_rrlayer *rr = &ms->rrlayer; - struct gsm_settings *set = &rr->ms->settings; + struct gsm_settings *set = &ms->settings; if (rr->state != GSM48_RR_ST_DEDICATED) return -EINVAL; @@ -343,19 +459,21 @@ const char *gsm48_rr_state_names[] = { static void new_rr_state(struct gsm48_rrlayer *rr, int state) { + struct osmocom_ms *ms = rr->ms; + if (state < 0 || state >= (sizeof(gsm48_rr_state_names) / sizeof(char *))) return; - /* must check against equal state */ - if (rr->state == state) { + /* Check against equal state or IDLE state. */ + if (rr->state == state && state != GSM48_RR_ST_IDLE) { LOGP(DRR, LOGL_INFO, "equal state ? %s\n", gsm48_rr_state_names[rr->state]); return; } LOGP(DRR, LOGL_INFO, "new state %s -> %s\n", - gsm48_rr_state_names[rr->state], gsm48_rr_state_names[state]); + gsm48_rr_state_names[rr->state], gsm48_rr_state_names[state]); /* abort handover, in case of release of dedicated mode */ if (rr->state == GSM48_RR_ST_DEDICATED) { @@ -369,10 +487,15 @@ static void new_rr_state(struct gsm48_rrlayer *rr, int state) rr->state = state; - if (state == GSM48_RR_ST_IDLE) { + if (state != GSM48_RR_ST_IDLE) + return; + + /* Return from dedicated/group receive/transmit mode to idle mode. (Trigger cell reselection.) */ + if (rr->vgcs.group_state == GSM48_RR_GST_OFF) { struct msgb *msg, *nmsg; struct gsm322_msg *em; + LOGP(DRR, LOGL_INFO, "Returning to IDLE mode.\n"); /* release dedicated mode, if any */ l1ctl_tx_dm_rel_req(rr->ms); rr->ms->meas.rl_fail = 0; @@ -411,6 +534,41 @@ static void new_rr_state(struct gsm48_rrlayer *rr, int state) msgb_free(nmsg); /* reset any BA range */ rr->ba_ranges = 0; + return; + } + + /* Return from dedicated mode to group receive mode. + * This is not used, because we never enter dedicated mode during group receive/transmit mode. + * It may be used later, if we support it in MM layer. */ + if (rr->vgcs.group_state == GSM48_RR_GST_RECEIVE) { + uint16_t ma[64]; + uint8_t ma_len; + struct msgb *msg; + + LOGP(DRR, LOGL_INFO, "Returning to GROUP RECEIVE mode.\n"); + /* release dedicated mode, if any */ + l1ctl_tx_dm_rel_req(rr->ms); + rr->ms->meas.rl_fail = 0; + rr->dm_est = 0; + l1ctl_tx_reset_req(rr->ms, L1CTL_RES_T_SCHED); + /* free establish message, if any */ + rr->rr_est_req = 0; + if (rr->rr_est_msg) { + msgb_free(rr->rr_est_msg); + rr->rr_est_msg = NULL; + } + /* free all pending messages */ + while ((msg = msgb_dequeue(&rr->downqueue))) + msgb_free(msg); + /* reset ciphering */ + rr->cipher_on = 0; + /* copy channel description "group mode" */ + memcpy(&rr->cd_now, &rr->vgcs.cd_group, sizeof(rr->cd_now)); + /* render channel "group mode" */ + gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len); + /* activate channel */ + gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len); + return; } } @@ -451,6 +609,14 @@ static const struct value_string gsm48_rr_msg_names[] = { { GSM48_RR_ABORT_REQ, "RR_ABORT_REQ" }, { GSM48_RR_ABORT_IND, "RR_ABORT_IND" }, { GSM48_RR_ACT_REQ, "RR_ACT_REQ" }, + { GSM48_RR_GROUP_REQ, "RR_GROUP_REQ" }, + { GSM48_RR_GROUP_CNF, "RR_GROUP_CNF" }, + { GSM48_RR_GROUP_REL_REQ, "RR_GROUP_REL_REQ" }, + { GSM48_RR_GROUP_REL_IND, "RR_GROUP_REL_IND" }, + { GSM48_RR_UPLINK_REQ, "RR_UPLINK_REQ" }, + { GSM48_RR_UPLINK_CNF, "RR_UPLINK_CNF" }, + { GSM48_RR_UPLINK_REL_REQ, "RR_UPLINK_REL_REQ" }, + { GSM48_RR_UPLINK_REL_IND, "RR_UPLINK_REL_IND" }, { 0, NULL } }; @@ -515,8 +681,8 @@ int gsm48_rr_upmsg(struct osmocom_ms *ms, struct msgb *msg) } /* push rsl header and send (RSL-SAP) */ -static int gsm48_send_rsl(struct osmocom_ms *ms, uint8_t msg_type, - struct msgb *msg, uint8_t link_id) +int gsm48_send_rsl(struct osmocom_ms *ms, uint8_t msg_type, + struct msgb *msg, uint8_t link_id) { struct gsm48_rrlayer *rr = &ms->rrlayer; @@ -632,39 +798,43 @@ static void timeout_rr_meas(void *arg) struct gsm_settings *set = &rr->ms->settings; int rxlev, berr, snr; uint8_t ch_type, ch_subch, ch_ts; + struct osmo_strbuf sb; char text[256]; - /* don't monitor if no cell is selcted or if we scan neighbour cells */ + sb = (struct osmo_strbuf) { .buf = text, .len = sizeof(text) }; + + /* don't monitor if no cell is selected or if we scan neighbour cells */ if (!cs->selected || cs->neighbour) { - sprintf(text, "MON: not camping on serving cell"); + OSMO_STRBUF_PRINTF(sb, "MON: not camping on serving cell"); goto restart; } else if (!meas->frames) { - sprintf(text, "MON: no cell info"); + OSMO_STRBUF_PRINTF(sb, "MON: no cell info"); } else { rxlev = (meas->rxlev + meas->frames / 2) / meas->frames; berr = (meas->berr + meas->frames / 2) / meas->frames; snr = (meas->snr + meas->frames / 2) / meas->frames; - sprintf(text, "MON: f=%d lev=%s snr=%2d ber=%3d " - "LAI=%s %s %04x ID=%04x", cs->sel_arfcn, - gsm_print_rxlev(rxlev), berr, snr, - gsm_print_mcc(cs->sel_mcc), - gsm_print_mnc(cs->sel_mnc), cs->sel_lac, cs->sel_id); + OSMO_STRBUF_PRINTF(sb, "MON: f=%d lev=%s snr=%2d ber=%3d " + "CGI=%s", cs->sel_arfcn, + gsm_print_rxlev(rxlev), snr, berr, + osmo_cgi_name(&cs->sel_cgi)); if (rr->state == GSM48_RR_ST_DEDICATED) { - rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, - &ch_subch, &ch_ts); - sprintf(text + strlen(text), " TA=%d pwr=%d TS=%d", - rr->cd_now.ind_ta - set->alter_delay, - (set->alter_tx_power) ? set->alter_tx_power_value - : rr->cd_now.ind_tx_power, ch_ts); - if (ch_type == RSL_CHAN_SDCCH8_ACCH - || ch_type == RSL_CHAN_SDCCH4_ACCH) - sprintf(text + strlen(text), "/%d", ch_subch); + OSMO_STRBUF_PRINTF(sb, " TA=%d pwr=%d", + rr->cd_now.ind_ta - set->alter_delay, + (set->alter_tx_power) ? set->alter_tx_power_value + : rr->cd_now.ind_tx_power); + if (rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts) == 0) { + OSMO_STRBUF_PRINTF(sb, " TS=%d", ch_ts); + if (ch_type == RSL_CHAN_SDCCH8_ACCH + || ch_type == RSL_CHAN_SDCCH4_ACCH + || ch_type == RSL_CHAN_Lm_ACCHs) + OSMO_STRBUF_PRINTF(sb, "/%d", ch_subch); + } } else gsm322_meas(rr->ms, rxlev); } LOGP(DRR, LOGL_INFO, "%s\n", text); if (rr->monitor) - vty_notify(rr->ms, "%s\n", text); + l23_vty_ms_notify(rr->ms, "%s\n", text); if (rr->dm_est) gsm48_rr_tx_meas_rep(rr->ms); @@ -765,6 +935,36 @@ static void timeout_rr_t3126(void *arg) new_rr_state(rr, GSM48_RR_ST_IDLE); } +static void timeout_rr_t3128(void *arg) +{ + struct gsm48_rrlayer *rr = arg; + struct osmocom_ms *ms = rr->ms; + + LOGP(DRR, LOGL_INFO, "timer T3128 has fired\n"); + + LOGP(DRR, LOGL_NOTICE, "Failed to access uplink, uplink did not become free.\n"); + gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_UPLINK_BUSY); +} + +static void timeout_rr_t3130(void *arg) +{ + struct gsm48_rrlayer *rr = arg; + struct osmocom_ms *ms = rr->ms; + + LOGP(DRR, LOGL_INFO, "timer T3130 has fired\n"); + + gsm48_rr_uplink_access(ms, NULL); +} + +static void timeout_rr_t_ul_free(void *arg) +{ + struct gsm48_rrlayer *rr = arg; + + LOGP(DRR, LOGL_INFO, "uplink free timer has fired\n"); + rr->vgcs.uplink_free = false; + gsm48_rr_uplink_status(rr->ms, GSM48_MM_EVENT_UPLINK_BUSY); +} + static void start_rr_t_meas(struct gsm48_rrlayer *rr, int sec, int micro) { rr->t_meas.cb = timeout_rr_meas; @@ -817,6 +1017,33 @@ static void start_rr_t3126(struct gsm48_rrlayer *rr, int sec, int micro) osmo_timer_schedule(&rr->t3126, sec, micro); } +static void start_rr_t3128(struct gsm48_rrlayer *rr, int sec, int micro) +{ + LOGP(DRR, LOGL_INFO, "starting T3128 with %d.%03d seconds\n", sec, + micro / 1000); + rr->vgcs.t3128.cb = timeout_rr_t3128; + rr->vgcs.t3128.data = rr; + osmo_timer_schedule(&rr->vgcs.t3128, sec, micro); +} + +static void start_rr_t3130(struct gsm48_rrlayer *rr, int sec, int micro) +{ + LOGP(DRR, LOGL_INFO, "starting T3130 with %d.%03d seconds\n", sec, + micro / 1000); + rr->vgcs.t3130.cb = timeout_rr_t3130; + rr->vgcs.t3130.data = rr; + osmo_timer_schedule(&rr->vgcs.t3130, sec, micro); +} + +static void start_rr_t_ul_free(struct gsm48_rrlayer *rr) +{ + if (!osmo_timer_pending(&rr->vgcs.t_ul_free)) + LOGP(DRR, LOGL_INFO, "starting uplink free timer\n"); + rr->vgcs.t_ul_free.cb = timeout_rr_t_ul_free; + rr->vgcs.t_ul_free.data = rr; + osmo_timer_schedule(&rr->vgcs.t_ul_free, 0, 480000); +} + static void stop_rr_t_meas(struct gsm48_rrlayer *rr) { if (osmo_timer_pending(&rr->t_meas)) { @@ -873,12 +1100,36 @@ static void stop_rr_t3126(struct gsm48_rrlayer *rr) } } +static void stop_rr_t3128(struct gsm48_rrlayer *rr) +{ + if (osmo_timer_pending(&rr->vgcs.t3128)) { + LOGP(DRR, LOGL_INFO, "stopping pending timer T3128\n"); + osmo_timer_del(&rr->vgcs.t3128); + } +} + +static void stop_rr_t3130(struct gsm48_rrlayer *rr) +{ + if (osmo_timer_pending(&rr->vgcs.t3130)) { + LOGP(DRR, LOGL_INFO, "stopping pending timer T3130\n"); + osmo_timer_del(&rr->vgcs.t3130); + } +} + +static void stop_rr_t_ul_free(struct gsm48_rrlayer *rr) +{ + if (osmo_timer_pending(&rr->vgcs.t_ul_free)) { + LOGP(DRR, LOGL_INFO, "stopping pending uplink free timer\n"); + osmo_timer_del(&rr->vgcs.t_ul_free); + } +} + /* * status */ /* send rr status request */ -static int gsm48_rr_tx_rr_status(struct osmocom_ms *ms, uint8_t cause) +int gsm48_rr_tx_rr_status(struct osmocom_ms *ms, uint8_t cause) { struct msgb *nmsg; struct gsm48_hdr *gh; @@ -893,7 +1144,7 @@ static int gsm48_rr_tx_rr_status(struct osmocom_ms *ms, uint8_t cause) st = (struct gsm48_rr_status *) msgb_put(nmsg, sizeof(*st)); gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_CIPH_M_COMPL; + gh->msg_type = GSM48_MT_RR_STATUS; /* rr cause */ st->rr_cause = cause; @@ -908,11 +1159,9 @@ static int gsm48_rr_tx_rr_status(struct osmocom_ms *ms, uint8_t cause) /* send chiperhing mode complete */ static int gsm48_rr_tx_cip_mode_cpl(struct osmocom_ms *ms, uint8_t cr) { - struct gsm_settings *set = &ms->settings; struct msgb *nmsg; struct gsm48_hdr *gh; struct gsm48_rr_hdr *nrrh; - uint8_t buf[11], *tlv; LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMPLETE (cr %d)\n", cr); @@ -925,13 +1174,8 @@ static int gsm48_rr_tx_cip_mode_cpl(struct osmocom_ms *ms, uint8_t cr) gh->msg_type = GSM48_MT_RR_CIPH_M_COMPL; /* MI */ - if (cr) { - gsm48_generate_mid_from_imsi(buf, set->imeisv); - /* alter MI type */ - buf[2] = (buf[2] & ~GSM_MI_TYPE_MASK) | GSM_MI_TYPE_IMEISV; - tlv = msgb_put(nmsg, 2 + buf[1]); - memcpy(tlv, buf, 2 + buf[1]); - } + if (cr) + gsm48_encode_mi_tlv(ms, nmsg, GSM_MI_TYPE_IMEISV, false); gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg, 0); @@ -1278,6 +1522,648 @@ static int gsm48_rr_rx_cm_enq(struct osmocom_ms *ms, struct msgb *msg) } /* + * ASCI notification + */ + +struct asci_notif { + struct llist_head entry; + struct gsm48_rrlayer *rr; + uint8_t gcr[5]; + bool ch_desc_present; + struct gsm48_chan_desc ch_desc; + struct osmo_timer_list timer; +}; + +/* When does a notification received from NCH expires. */ +#define NOTIFICATION_TIMEOUT 5 + +static void asci_notif_timeout(void *arg); + +/* Add new notification to list. */ +static struct asci_notif *asci_notif_alloc(struct gsm48_rrlayer *rr, const uint8_t *gcr) +{ + struct asci_notif *notif; + + notif = talloc_zero(rr->ms, struct asci_notif); + if (!notif) + return NULL; + notif->rr = rr; + memcpy(notif->gcr, gcr, sizeof(notif->gcr)); + llist_add_tail(¬if->entry, &rr->vgcs.notif_list); + + notif->timer.cb = asci_notif_timeout; + notif->timer.data = notif; + + return notif; +} + +/* Remove notification from list. */ +static void asci_notif_free(struct asci_notif *notif) +{ + osmo_timer_del(¬if->timer); + llist_del(¬if->entry); + talloc_free(notif); +} + +/* Remove all ASCI notifications from list. */ +static void asci_notif_list_free(struct gsm48_rrlayer *rr) +{ + struct asci_notif *notif, *notif2; + + llist_for_each_entry_safe(notif, notif2, &rr->vgcs.notif_list, entry) + asci_notif_free(notif); +} + +/* Notification timed out. */ +static void asci_notif_timeout(void *arg) +{ + struct asci_notif *notif = arg; + struct gsm48_rrlayer *rr = notif->rr; + struct msgb *nmsg; + struct gsm48_mm_event *mme; + + /* Send notification of ceased call to MM layer. */ + LOGP(DRR, LOGL_INFO, "Notify MM layer about ceased group call.\n"); + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NOTIFICATION); + if (!nmsg) + return; + mme = (struct gsm48_mm_event *) nmsg->data; + memcpy(mme->notification.gcr, notif->gcr, sizeof(mme->notification.gcr)); + mme->notification.gone = true; + gsm48_mmevent_msg(rr->ms, nmsg); + + asci_notif_free(notif); +} + +/* Find notification in list. */ +static struct asci_notif *asci_notif_find(struct gsm48_rrlayer *rr, const uint8_t *gcr) +{ + struct asci_notif *notif; + + llist_for_each_entry(notif, &rr->vgcs.notif_list, entry) { + if (!memcmp(¬if->gcr, gcr, sizeof(notif->gcr))) + return notif; + } + + return NULL; +} + +static int gsm48_rr_rx_group_call(struct osmocom_ms *ms, const uint8_t *gcr, const uint8_t *ch_desc, bool nch) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct asci_notif *notif; + bool update_call = false; + struct msgb *nmsg; + struct gsm48_mm_event *mme; + + /* Find or create notification entry. Only create entries for notifications on NCH */ + notif = asci_notif_find(rr, gcr); + if (!notif) { + update_call = true; + if (nch) { + notif = asci_notif_alloc(rr, gcr); + if (!notif) + return -ENOMEM; + } + } + + /* Update channel description. */ + if (notif) { + if (ch_desc) { + if (!notif->ch_desc_present) + update_call = true; + notif->ch_desc_present = true; + memcpy(¬if->ch_desc, ch_desc, sizeof(notif->ch_desc)); + } else { + notif->ch_desc_present = false; + if (!notif->ch_desc_present) + update_call = true; + } + /* (Re-)Start timer. */ + osmo_timer_schedule(¬if->timer, NOTIFICATION_TIMEOUT, 0); + } else + update_call = true; + + /* Send notification of new or updated call to MM layer. */ + if (update_call) { + LOGP(DRR, LOGL_INFO, "Notify MM layer about new/updated group call.\n"); + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NOTIFICATION); + if (!nmsg) + return -ENOMEM; + mme = (struct gsm48_mm_event *) nmsg->data; + memcpy(mme->notification.gcr, gcr, sizeof(mme->notification.gcr)); + if (ch_desc) { + mme->notification.ch_desc_present = true; + memcpy(&mme->notification.ch_desc, ch_desc, sizeof(mme->notification.ch_desc)); + } + gsm48_mmevent_msg(ms, nmsg); + } + + return 0; +} + +/* Common function to decode Group Call Information */ +static int gsm48_rr_decode_group_call_info(struct osmocom_ms *ms, struct bitvec *bv, bool nch) +{ + struct bitvec *gcr_bv = NULL, *chd_bv = NULL; + int rc = 0; + int i; + + /* <Group Call Reference : bit(36)> */ + gcr_bv = bitvec_alloc(OSMO_BYTES_FOR_BITS(36), NULL); + OSMO_ASSERT(gcr_bv); + for (i = 0; i < 36; i++) + bitvec_set_bit(gcr_bv, bitvec_get_bit_pos(bv, bv->cur_bit++)); + /* Group Channel Description */ + if (bitvec_get_bit_pos(bv, bv->cur_bit++) == 1) { + chd_bv = bitvec_alloc(OSMO_BYTES_FOR_BITS(24), NULL); + OSMO_ASSERT(chd_bv); + for (i = 0; i < 24; i++) + bitvec_set_bit(chd_bv, bitvec_get_bit_pos(bv, bv->cur_bit++)); + /* FIXME: hopping */ + if (bitvec_get_bit_pos(bv, bv->cur_bit++) == 1) { + LOGP(DRR, LOGL_ERROR, "Hopping not supported on VGCS/VBS channel, please fix!\n"); + rc = -ENOTSUP; + goto out; + } + } + + rc = gsm48_rr_rx_group_call(ms, gcr_bv->data, (chd_bv) ? chd_bv->data : NULL, nch); + +out: + bitvec_free(chd_bv); + bitvec_free(gcr_bv); + + return rc; +} + +/* Notification/FACCH (9.1.21a) */ +static int gsm48_rr_rx_notif_facch(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_hdr_sh *sgh = msgb_l3(msg); + int payload_len = msgb_l3len(msg) - sizeof(*sgh); + struct bitvec bv; + int rc; + + LOGP(DRR, LOGL_INFO, "NOTIFICATION/FACCH\n"); + + bv = (struct bitvec) { + .data = sgh->data, + .data_len = payload_len, + }; + + /* Group Call Information */ + if (bitvec_get_bit_pos(&bv, bv.cur_bit++) == 0) { + rc = gsm48_rr_decode_group_call_info(ms, &bv, false); + return rc; + } + + /* Note: Other information are not used. */ + return -ENOTSUP; +} + +/* Notification/NCH (9.1.21b) */ +static int gsm48_rr_rx_notif_nch(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_notification_nch *nn = msgb_l3(msg); + int payload_len = msgb_l3len(msg) - sizeof(*nn); + struct bitvec bv; + int rc; + + LOGP(DRR, LOGL_INFO, "NOTIFICATION/NCH\n"); + + bv = (struct bitvec) { + .data = nn->data, + .data_len = payload_len, + }; + + /* 0 | 1 <NLN(NCH) : bit (2) > */ + if (bitvec_get_bit_pos(&bv, bv.cur_bit++) == 1) { + /* NLN not used + nln = bitvec_get_uint(&bv, 2); + nln_present = true; + */ + bv.cur_bit += 2; + } + + /* < list of Group Call NCH information > */ + while (bitvec_get_bit_pos(&bv, bv.cur_bit++) == 1) { + rc = gsm48_rr_decode_group_call_info(ms, &bv, true); + if (rc < 0) + break; + } + + return 0; +} + +/* + * VGCS uplink control + */ + +/* Send uplink status to upper layer. */ +static int gsm48_rr_uplink_status(struct osmocom_ms *ms, uint32_t event) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + + if (rr->vgcs.group_state != GSM48_RR_GST_RECEIVE) + return -EINVAL; + + /* Send notification of uplink state to MM layer. */ + LOGP(DRR, LOGL_INFO, "Notify MM layer about uplink state.\n"); + nmsg = gsm48_mmevent_msgb_alloc(event); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(rr->ms, nmsg); + + return 0; +} + +/* UPLINK BUSY (9.1.46) */ +static int gsm48_rr_rx_uplink_busy(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + LOGP(DRR, LOGL_INFO, "UPLINK BUSY\n"); + + /* Only allow when in some group mode. */ + if (rr->vgcs.group_state == GSM48_RR_GST_OFF) + return 0; + + /* Uplink is busy now. */ + if (rr->vgcs.uplink_free) { + rr->vgcs.uplink_free = false; + gsm48_rr_uplink_status(ms, GSM48_MM_EVENT_UPLINK_BUSY); + } + stop_rr_t_ul_free(rr); + + /* Abort during uplink investigation or access procedure. */ + if (osmo_timer_pending(&rr->vgcs.t3128) || osmo_timer_pending(&rr->vgcs.t3130)) { + LOGP(DRR, LOGL_NOTICE, "Abort uplink access, due to busy uplink.\n"); + return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_UPLINK_BUSY); + } + + return 0; +} + +/* UPLINK FREE (9.1.47) */ +static int gsm48_rr_rx_uplink_free(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct bitvec bv; + bool uplink_access = false; + uint8_t uic = 0xff; + uint8_t *mode; + + bv = (struct bitvec) { + .data_len = msgb_l3len(msg), + .data = msgb_l3(msg), + .cur_bit = 8, + }; + + /* Uplink Access */ + if (bitvec_get_bit_high(&bv) == H) + uplink_access = true; + + /* UIC */ + if (bitvec_get_bit_high(&bv) == H) + uic = bitvec_get_uint(&bv, 6); + + /* Note: Emergency Indicator not used. */ + + /* Do not flood the logging with UPLINK FREE messages. Log only on the fist received message. */ + if (!osmo_timer_pending(&rr->vgcs.t_ul_free)) + LOGP(DRR, LOGL_INFO, "UPLINK FREE (uplink access=%s, uic=0x%02x)\n", (uplink_access) ? "true" : "false", + uic); + + /* Uplink is free now. */ + if (!rr->vgcs.uplink_free) { + rr->vgcs.uplink_free = true; + gsm48_rr_uplink_status(ms, GSM48_MM_EVENT_UPLINK_FREE); + } + rr->vgcs.uic = uic; + rr->vgcs.uplink_access = uplink_access; + start_rr_t_ul_free(rr); + + /* We can be in group mode or in dedicated mode. When we are in dedicated mode and we receive UPLINK FREE, + * we know that we are actually on a group channel. This is the actual confirm to the UPLINK RELEASE message + * on a group channel. We must then release layer 2 locally and indicate channel release toward upper layer. + * + * When we are in group transmit mode, we return to group receive mode and also release layer 2 locally and + * indicate uplink release towards upper layer. + * + * When we are waiting for a free uplink (T3128 is running), we start uplink access. While accessing the + * uplink, we ignore further UPLINK FREE messages. + */ + if (rr->vgcs.group_state != GSM48_RR_GST_OFF) { + /* Start uplink access. */ + if (osmo_timer_pending(&rr->vgcs.t3128)) { + /* Stop timer, because uplink is now free. */ + stop_rr_t3128(rr); + rr->vgcs.uplink_tries = 3; + return gsm48_rr_uplink_access(ms, NULL); + } + + /* Ignore uplink free messages while accessing uplink. */ + if (osmo_timer_pending(&rr->vgcs.t3130)) + return 0; + } + + /* Any time in group transmit mode or dedicated mode, release on uplink free. */ + if (rr->state == GSM48_RR_ST_DEDICATED || rr->state == GSM48_RR_ST_REL_PEND) { + struct msgb *nmsg; + + LOGP(DRR, LOGL_INFO, "Uplink becomes free, send (local) release to layer 2.\n"); + + /* Go into pending release state if not already. + * We are already in pending relese state when we release uplink normally. + * If we receive UPLINK FREE while we release normally, we abort layer 2. */ + if (rr->state != GSM48_RR_ST_REL_PEND) + 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] = RSL_REL_LOCAL_END; + gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0); + + return 0; + } + + return 0; +} + +/* UPLINK RELEASE (9.1.48) */ +static int gsm48_rr_rx_uplink_release(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_uplink_release *ur = (struct gsm48_uplink_release *)gh->data; + int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ur); + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short UPLINK RELEASE message.\n"); + return -EINVAL; + } + + LOGP(DRR, LOGL_INFO, "UPLINK RELEASE with cause 0x%02x\n", ur->rr_cause); + + /* Only allow when in some group mode. */ + if (rr->vgcs.group_state == GSM48_RR_GST_OFF) + return 0; + + if (rr->state == GSM48_RR_ST_DEDICATED && rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) + return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_NORMAL); + + return 0; +} + +/* VGCS UPLINK GRANT (9.1.49) */ +static int gsm48_rr_rx_vgcs_uplink_grant(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_sysinfo *s = &ms->cellsel.sel_si; + struct gsm_settings *set = &ms->settings; + struct gsm0408_vgcs_ul_grant *ug = msgb_l3(msg); + int ug_len = msgb_l3len(msg) - sizeof(*ug); + + LOGP(DRR, LOGL_INFO, "VGCS UPLINK GRANT\n"); + + if (ug_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of VGCS UPLINK GRANT message.\n"); + return -EINVAL; + } + + /* Only allow when in some group mode. */ + if (rr->vgcs.group_state == GSM48_RR_GST_OFF) + return 0; + + /* Uplink is busy now. */ + if (rr->vgcs.uplink_free) { + rr->vgcs.uplink_free = false; + gsm48_rr_uplink_status(rr->ms, GSM48_MM_EVENT_UPLINK_BUSY); + } + stop_rr_t_ul_free(rr); + + /* Abort during uplink investigation or access procedure. */ + if (osmo_timer_pending(&rr->vgcs.t3128)) { + LOGP(DRR, LOGL_NOTICE, "Abort uplink access, other phone accessing the uplink.\n"); + return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_UPLINK_BUSY); + } + + /* We are not waiting for the uplink to be granted. */ + if (!osmo_timer_pending(&rr->vgcs.t3130)) + return 0; + + /* Stop timer. */ + stop_rr_t3130(rr); + + /* Check if message is for our request. */ + if (!gsm48_match_ra(ms, &ug->req_ref, 5)) { + LOGP(DRR, LOGL_NOTICE, "Abort uplink access, other phone gets uplink access granted.\n"); + return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_UPLINK_BUSY); + } + + LOGP(DRR, LOGL_INFO, "Access to uplink has been granted.\n"); + + /* Set initial power and timing advance. */ + rr->cd_now.ind_tx_power = s->ms_txpwr_max_cch; + rr->cd_now.ind_ta = ug->ta; + LOGP(DRR, LOGL_INFO, "Applying initial ta and tx_power\n"); + l1ctl_tx_param_req(ms, rr->cd_now.ind_ta - set->alter_delay, + (set->alter_tx_power) ? set->alter_tx_power_value : s->ms_txpwr_max_cch); + + /* Turn on transmitter. */ + rr->cd_now.tch_flags &= ~(L1CTL_TCH_FLAG_RXONLY); + gsm48_rr_set_mode(ms, rr->cd_now.chan_nr, rr->cd_now.mode, rr->cd_now.tch_flags); + + /* Complete group transmit mode. */ + new_rr_state(rr, GSM48_RR_ST_DEDICATED); + + /* Establish layer 2 connection. */ + return gsm48_rr_tx_talker_indication(ms); +} + +/* send rr uplink release */ +static int gsm48_rr_tx_uplink_release(struct osmocom_ms *ms, uint8_t cause) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_uplink_release *ur; + + LOGP(DRR, LOGL_INFO, "UPLINK RELEASE (cause #%d)\n", cause); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + ur = (struct gsm48_uplink_release *) msgb_put(nmsg, sizeof(*ur)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_UPLINK_RELEASE; + ur->rr_cause = cause; + + return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg, 0); +} + +/* Start uplink access procedure. (3.3.1.2.1.2) */ +static int gsm48_rr_uplink_access(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + /* store frame number */ + if (msg) { + struct abis_rsl_cchan_hdr *ch = msgb_l2(msg); + struct gsm48_req_ref *ref = + (struct gsm48_req_ref *) (ch->data + 1); + + if (msgb_l2len(msg) < sizeof(*ch) + sizeof(*ref)) { + LOGP(DRR, LOGL_ERROR, "CHAN_CNF too slort\n"); + return -EINVAL; + } + + /* Store to history buffer. */ + /* shift history and store */ + memcpy(&(rr->cr_hist[4]), &(rr->cr_hist[3]), + sizeof(struct gsm48_cr_hist)); + memcpy(&(rr->cr_hist[3]), &(rr->cr_hist[2]), + sizeof(struct gsm48_cr_hist)); + memcpy(&(rr->cr_hist[2]), &(rr->cr_hist[1]), + sizeof(struct gsm48_cr_hist)); + memcpy(&(rr->cr_hist[1]), &(rr->cr_hist[0]), + sizeof(struct gsm48_cr_hist)); + rr->cr_hist[0].valid = 1; + rr->cr_hist[0].ref.ra = rr->cr_ra; + rr->cr_hist[0].ref.t1 = ref->t1; + rr->cr_hist[0].ref.t2 = ref->t2; + rr->cr_hist[0].ref.t3_low = ref->t3_low; + rr->cr_hist[0].ref.t3_high = ref->t3_high; + } + + if (!osmo_timer_pending(&rr->vgcs.t3130)) { + uint8_t uplink_ref; + + /* Only try up to 3 times. */ + if (!rr->vgcs.uplink_tries) { + LOGP(DRR, LOGL_NOTICE, "Abort uplink access, due uplink access timeout.\n"); + return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_LINK_FAILURE); + } + + LOGP(DRR, LOGL_INFO, "Trying to access uplink.\n"); + + rr->vgcs.uplink_tries--; + + /* See Table 9.1.45.1 */ + uplink_ref = layer23_random(); + uplink_ref &= 0x1f; + uplink_ref |= 0xc0; + + /* store value, mask and history */ + rr->cr_ra = uplink_ref; + rr->cr_hist[4].valid = 0; + rr->cr_hist[3].valid = 0; + rr->cr_hist[2].valid = 0; + rr->cr_hist[1].valid = 0; + rr->cr_hist[0].valid = 0; + + /* Reset counter. */ + rr->vgcs.uplink_counter = 0; + + /* Start T3130. */ + start_rr_t3130(rr, GSM_T3130_MS); + } + + /* Send random access bursts up to 5 times. */ + if (rr->vgcs.uplink_counter < 5) { + int delay_ms; + + /* The first UPLINK ACCESS message shall be delayed between 0..20ms. + * Subsequent UPLINK ACCESS messages shall be delayed 100ms + 0..20ms. */ + delay_ms = (layer23_random() & 0xffff) / 3277; + if (rr->vgcs.uplink_counter) + delay_ms += 100; + + gsm48_rr_tx_rand_acc_dedicated(ms, rr->cr_ra, delay_ms * 26 / 120, rr->vgcs.uic); + rr->vgcs.uplink_counter++; + } + + return 0; +} + +/* Whenever uplink access is released or failed for some reason. */ +static int gsm48_rr_uplink_access_abort(struct osmocom_ms *ms, uint8_t cause) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + + /* Stop group transmit mode timers. */ + stop_rr_t3128(rr); + stop_rr_t3130(rr); + + /* Turn off transmitter. */ + rr->cd_now.tch_flags |= L1CTL_TCH_FLAG_RXONLY; + gsm48_rr_set_mode(ms, rr->cd_now.chan_nr, rr->cd_now.mode, rr->cd_now.tch_flags); + + /* Only return IDLE without changing channel, because we are still in group transmit mode. */ + new_rr_state(rr, GSM48_RR_ST_IDLE); + + /* Set group state to receive mode. */ + rr->vgcs.group_state = GSM48_RR_GST_RECEIVE; + + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_UPLINK_REL_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = cause; + return gsm48_rr_upmsg(ms, nmsg); +} + +/* send talker indication */ +static int gsm48_rr_tx_talker_indication(struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm322_cellsel *cs = &ms->cellsel; + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_talker_indication *ti; + + LOGP(DRR, LOGL_INFO, "TALKER INDICATION\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + ti = (struct gsm48_talker_indication *) msgb_put(nmsg, sizeof(*ti)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_TALKER_IND; + + /* classmark 2 */ + ti->cm2_len = sizeof(ti->cm2); + gsm48_rr_enc_cm2(ms, &ti->cm2, rr->cd_now.arfcn); + + /* mobile identity */ + if (ms->subscr.tmsi != GSM_RESERVED_TMSI && (osmo_lai_cmp(&subscr->lai, &cs->sel_cgi.lai) == 0)) { + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_TMSI, false); + LOGP(DRR, LOGL_INFO, "Sending TALKER INDICATION with TMSI.\n"); + } else if (subscr->imsi[0]) { + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, false); + LOGP(DRR, LOGL_INFO, "Sending TALKER INDICATION with IMSI.\n"); + } else { + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_NONE, false); + LOGP(DRR, LOGL_INFO, "Sending TALKER INDICATION without TMSI/IMSI.\n"); + } + + /* start establishmnet */ + return gsm48_send_rsl(ms, RSL_MT_EST_REQ, nmsg, 0); +} + +/* * random access */ @@ -1302,7 +2188,7 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging, && (!cs->selected || (cs->state != GSM322_C3_CAMPED_NORMALLY && cs->state != GSM322_C7_CAMPED_ANY_CELL))) { LOGP(DRR, LOGL_INFO, "Paging, but not camping, ignore.\n"); - return -EINVAL; + return -EINVAL; } /* ignore channel request while not camping on a cell */ @@ -1454,7 +2340,7 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging, if (s->neci) { chan_req_mask = 0x0f; chan_req_val = 0x10; - LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (OHTER " + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (OTHER " "with NECI)\n", chan_req_val); } else { chan_req_mask = 0x1f; @@ -1483,12 +2369,10 @@ rel_ind: return -EINVAL; } - /* store value, mask and history */ + /* store value, mask and clear history */ rr->chan_req_val = chan_req_val; rr->chan_req_mask = chan_req_mask; - rr->cr_hist[2].valid = 0; - rr->cr_hist[1].valid = 0; - rr->cr_hist[0].valid = 0; + memset(rr->cr_hist, 0, sizeof(rr->cr_hist)); /* store establishment cause, so 'choose cell' selects the last cell * after location updating */ @@ -1518,7 +2402,10 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg) uint8_t tx_power; enum gsm_band band; - gsm_arfcn2band_rc(cs->arfcn, &band); + if (gsm_arfcn2band_rc(cs->arfcn, &band) != 0) { + LOGP(DRR, LOGL_ERROR, "gsm_arfcn2band_rc() failed\n"); + return -EINVAL; + } /* already assigned */ if (rr->wait_assign == 2) @@ -1536,10 +2423,7 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg) } /* shift history and store */ - memcpy(&(rr->cr_hist[2]), &(rr->cr_hist[1]), - sizeof(struct gsm48_cr_hist)); - memcpy(&(rr->cr_hist[1]), &(rr->cr_hist[0]), - sizeof(struct gsm48_cr_hist)); + memmove(rr->cr_hist + 1, rr->cr_hist, sizeof(rr->cr_hist) - sizeof(rr->cr_hist[0])); rr->cr_hist[0].valid = 1; rr->cr_hist[0].ref.ra = rr->cr_ra; rr->cr_hist[0].ref.t1 = ref->t1; @@ -1593,11 +2477,11 @@ fail: rr->n_chan_req--; if (rr->wait_assign == 0) { - /* first random acces, without delay of slots */ + /* first random access, without delay of slots */ slots = 0; rr->wait_assign = 1; } else { - /* subsequent random acces, with slots from table 3.1 */ + /* subsequent random access, with slots from table 3.1 */ switch(s->tx_integer) { case 3: case 8: case 14: case 50: if (s->ccch_conf != 1) /* not combined CCCH */ @@ -1707,32 +2591,31 @@ static int gsm48_new_sysinfo(struct osmocom_ms *ms, uint8_t type) && s->si5 && (!s->nb_ext_ind_si5 || s->si5bis)) { struct gsm48_rr_meas *rrmeas = &ms->rrlayer.meas; - int n = 0, i, refer_pcs; + int i; + bool refer_pcs; + int16_t arfcn; LOGP(DRR, LOGL_NOTICE, "Complete set of SI5* for BA(%d)\n", s->nb_ba_ind_si5); rrmeas->nc_num = 0; refer_pcs = gsm_refer_pcs(cs->arfcn, s); - /* collect channels from freq list (1..1023,0) */ - for (i = 1; i <= 1024; i++) { - if ((s->freq[i & 1023].mask & FREQ_TYPE_REP)) { - if (n == 32) { - LOGP(DRR, LOGL_NOTICE, "SI5* report " - "exceeds 32 BCCHs\n"); - break; - } - if (refer_pcs && i >= 512 && i <= 810) - rrmeas->nc_arfcn[n] = i | ARFCN_PCS; - else - rrmeas->nc_arfcn[n] = i & 1023; - rrmeas->nc_rxlev_dbm[n] = -128; - LOGP(DRR, LOGL_NOTICE, "SI5* report arfcn %s\n", - gsm_print_arfcn(rrmeas->nc_arfcn[n])); - n++; - } + /* Collect channels from freq list in correct order. */ + for (i = 0; i < 32; i++) { + arfcn = arfcn_from_freq_index(s, i); + if (arfcn < 0) + break; + if (refer_pcs && arfcn >= 512 && arfcn <= 810) + rrmeas->nc_arfcn[i] = arfcn | ARFCN_PCS; + else + rrmeas->nc_arfcn[i] = arfcn; + rrmeas->nc_rxlev_dbm[i] = -128; + LOGP(DRR, LOGL_NOTICE, "SI5/SI5bis report arfcn %s (index %d)\n", + gsm_print_arfcn(rrmeas->nc_arfcn[i]), i); } - rrmeas->nc_num = n; + rrmeas->nc_num = i; + if (i == 32 && arfcn_from_freq_index(s, i) >= 0) + LOGP(DRR, LOGL_NOTICE, "SI5/SI5bis/SI5ter define more than 32 channels.\n"); } /* send sysinfo event to other layers */ @@ -1777,7 +2660,7 @@ static int gsm48_rr_rx_sysinfo1(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si1_msg, MIN(msgb_l3len(msg), sizeof(s->si1_msg)))) + if (!memcmp(si, s->si1_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si1_msg)))) return 0; gsm48_decode_sysinfo1(s, si, msgb_l3len(msg)); @@ -1806,7 +2689,7 @@ static int gsm48_rr_rx_sysinfo2(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si2_msg, MIN(msgb_l3len(msg), sizeof(s->si2_msg)))) + if (!memcmp(si, s->si2_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si2_msg)))) return 0; gsm48_decode_sysinfo2(s, si, msgb_l3len(msg)); @@ -1835,7 +2718,7 @@ static int gsm48_rr_rx_sysinfo2bis(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si2b_msg, MIN(msgb_l3len(msg), sizeof(s->si2b_msg)))) + if (!memcmp(si, s->si2b_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si2b_msg)))) return 0; gsm48_decode_sysinfo2bis(s, si, msgb_l3len(msg)); @@ -1864,7 +2747,7 @@ static int gsm48_rr_rx_sysinfo2ter(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si2t_msg, MIN(msgb_l3len(msg), sizeof(s->si2t_msg)))) + if (!memcmp(si, s->si2t_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si2t_msg)))) return 0; gsm48_decode_sysinfo2ter(s, si, msgb_l3len(msg)); @@ -1894,7 +2777,7 @@ static int gsm48_rr_rx_sysinfo3(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si3_msg, MIN(msgb_l3len(msg), sizeof(s->si3_msg)))) + if (!memcmp(si, s->si3_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si3_msg)))) return 0; gsm48_decode_sysinfo3(s, si, msgb_l3len(msg)); @@ -1929,14 +2812,12 @@ static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si4_msg, MIN(msgb_l3len(msg), sizeof(s->si4_msg)))) + if (!memcmp(si, s->si4_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si4_msg)))) return 0; gsm48_decode_sysinfo4(s, si, msgb_l3len(msg)); - LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 4 (mcc %s mnc %s " - "lac 0x%04x)\n", gsm_print_mcc(s->mcc), - gsm_print_mnc(s->mnc), s->lac); + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 4 (lai=%s)\n", osmo_lai_name(&s->lai)); return gsm48_new_sysinfo(ms, si->header.system_information); } @@ -1944,9 +2825,10 @@ static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg) /* receive "SYSTEM INFORMATION 5" message (9.1.37) */ static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg) { - struct gsm48_system_information_type_5 *si = msgb_l3(msg); + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_5 *si = msgb_l3(msg) + 1; struct gsm48_sysinfo *s = ms->cellsel.si; - int payload_len = msgb_l3len(msg) - sizeof(*si); + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; if (!s) { LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5 " @@ -1960,7 +2842,7 @@ static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si5_msg, MIN(msgb_l3len(msg), sizeof(s->si5_msg)))) + if (!memcmp(si, s->si5_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si5_msg)))) return 0; gsm48_decode_sysinfo5(s, si, msgb_l3len(msg)); @@ -1973,9 +2855,10 @@ static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg) /* receive "SYSTEM INFORMATION 5bis" message (9.1.38) */ static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg) { - struct gsm48_system_information_type_5bis *si = msgb_l3(msg); + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_5bis *si = msgb_l3(msg) + 1; struct gsm48_sysinfo *s = ms->cellsel.si; - int payload_len = msgb_l3len(msg) - sizeof(*si); + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; if (!s) { LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5bis" @@ -1989,7 +2872,7 @@ static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si5b_msg, MIN(msgb_l3len(msg), + if (!memcmp(si, s->si5b_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si5b_msg)))) return 0; @@ -2003,9 +2886,10 @@ static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg) /* receive "SYSTEM INFORMATION 5ter" message (9.1.39) */ static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg) { - struct gsm48_system_information_type_5ter *si = msgb_l3(msg); + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_5ter *si = msgb_l3(msg) + 1; struct gsm48_sysinfo *s = ms->cellsel.si; - int payload_len = msgb_l3len(msg) - sizeof(*si); + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; if (!s) { LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5ter" @@ -2019,7 +2903,7 @@ static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si5t_msg, MIN(msgb_l3len(msg), + if (!memcmp(si, s->si5t_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si5t_msg)))) return 0; @@ -2033,10 +2917,11 @@ static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg) /* receive "SYSTEM INFORMATION 6" message (9.1.39) */ static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg) { - struct gsm48_system_information_type_6 *si = msgb_l3(msg); + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_6 *si = msgb_l3(msg) + 1; struct gsm48_sysinfo *s = ms->cellsel.si; struct rx_meas_stat *meas = &ms->meas; - int payload_len = msgb_l3len(msg) - sizeof(*si); + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; if (!s) { LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 6 " @@ -2050,14 +2935,13 @@ static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si6_msg, MIN(msgb_l3len(msg), sizeof(s->si6_msg)))) + if (!memcmp(si, s->si6_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si6_msg)))) return 0; gsm48_decode_sysinfo6(s, si, msgb_l3len(msg)); - LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 6 (mcc %s mnc %s " - "lac 0x%04x SACCH-timeout %d)\n", gsm_print_mcc(s->mcc), - gsm_print_mnc(s->mnc), s->lac, s->sacch_radio_link_timeout); + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 6 (lai=%s SACCH-timeout %d)\n", + osmo_lai_name(&s->lai), s->sacch_radio_link_timeout); meas->rl_fail = meas->s = s->sacch_radio_link_timeout; LOGP(DRR, LOGL_INFO, "using (new) SACCH timeout %d\n", meas->rl_fail); @@ -2065,6 +2949,72 @@ static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg) return gsm48_new_sysinfo(ms, si->system_information); } +/* Receive "SYSTEM INFORMATION 10" message (9.1.50). */ +static int gsm48_rr_rx_sysinfo_10(struct osmocom_ms *ms, struct msgb *msg) +{ + /* NOTE: Short L2 header is included in this structure */ + struct gsm48_system_information_type_10 *si = msgb_l3(msg); + struct gsm48_sysinfo *s = ms->cellsel.si; + int payload_len = msgb_l3len(msg) - sizeof(*si); + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 10 ignored.\n"); + return -EINVAL; + } + + if (payload_len < 20) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 10 message.\n"); + return -EINVAL; + } + + /* No complete SI5, cannot decode yet. */ + if (!s->si5 || !(s->si5bis || !s->nb_ext_ind_si5)) + return 0; + + /* We decode when changed or when SI10 could not decoded, due to missing neighbor cell infos. */ + if (!memcmp(si, s->si10_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si10_msg))) && s->si10) + return 0; + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 10\n"); + + gsm48_decode_sysinfo10(s, si, msgb_l3len(msg)); + + /* We cannot call gsm48_new_sysinfo, because it requires regular message types. */ + return 0; +} + +/* receive "SYSTEM INFORMATION 13" message (9.1.43a) */ +static int gsm48_rr_rx_sysinfo13(struct osmocom_ms *ms, struct msgb *msg) +{ + const struct gsm48_system_information_type_13 *si = msgb_l3(msg); + int rest_octets_len = msgb_l3len(msg) - sizeof(si->header); + struct gsm48_sysinfo *s = ms->cellsel.si; + + if (!s) { + LOGP(DRR, LOGL_INFO, + "No cell selected, SYSTEM INFORMATION 13 ignored\n"); + return -EINVAL; + } + + if (rest_octets_len < 0) { + LOGP(DRR, LOGL_NOTICE, + "Short read of SYSTEM INFORMATION 13 message.\n"); + return -EINVAL; + } + + if (!memcmp(si, s->si13_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si6_msg)))) + return 0; + + gsm48_decode_sysinfo13(s, si, msgb_l3len(msg)); + + LOGP(DRR, LOGL_INFO, + "New SYSTEM INFORMATION 13 (%s, RAC 0x%02x, NCO %u, MNO %u)\n", + s->gprs.egprs_supported ? "EGPRS" : "GPRS only", + s->gprs.rac, s->gprs.nco, s->gprs.nmo); + + return gsm48_new_sysinfo(ms, si->header.system_information); +} + /* * paging */ @@ -2077,46 +3027,38 @@ static int gsm48_rr_chan2cause[4] = { RR_EST_CAUSE_ANS_PAG_TCH_ANY }; -/* given LV of mobile identity is checked agains ms */ -static uint8_t gsm_match_mi(struct osmocom_ms *ms, uint8_t *mi) +/* given LV of mobile identity is checked against ms */ +static uint8_t gsm_match_mi(struct osmocom_ms *ms, const uint8_t *mi_lv) { struct gsm322_cellsel *cs = &ms->cellsel; - char imsi[16]; - uint32_t tmsi; - uint8_t mi_type; + struct osmo_mobile_identity mi; + char buf[32]; + int rc; - if (mi[0] < 1) - return 0; - mi_type = mi[1] & GSM_MI_TYPE_MASK; - switch (mi_type) { + rc = osmo_mobile_identity_decode(&mi, mi_lv+1, mi_lv[0], false); + if (rc < 0) + return rc; + osmo_mobile_identity_to_str_buf(buf, sizeof(buf), &mi); + + switch (mi.type) { case GSM_MI_TYPE_TMSI: - if (mi[0] < 5) - return 0; - memcpy(&tmsi, mi+2, 4); - if (ms->subscr.tmsi == ntohl(tmsi) - && ms->subscr.mcc == cs->sel_mcc - && ms->subscr.mnc == cs->sel_mnc - && ms->subscr.lac == cs->sel_lac) { - LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", - ntohl(tmsi)); - - return mi_type; + if ((ms->subscr.tmsi == mi.tmsi) + && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) { + LOGP(DPAG, LOGL_INFO, " %s matches\n", buf); + return mi.type; } else - LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n", - ntohl(tmsi)); + LOGP(DPAG, LOGL_INFO, " %s (not for us)\n", buf); break; case GSM_MI_TYPE_IMSI: - gsm48_mi_to_string(imsi, sizeof(imsi), mi + 1, mi[0]); - if (!strcmp(imsi, ms->subscr.imsi)) { - LOGP(DPAG, LOGL_INFO, " IMSI %s matches\n", imsi); - - return mi_type; + if (!strcmp(mi.imsi, ms->subscr.imsi)) { + LOGP(DPAG, LOGL_INFO, " %s matches\n", buf); + return mi.type; } else - LOGP(DPAG, LOGL_INFO, " IMSI %s (not for us)\n", imsi); + LOGP(DPAG, LOGL_INFO, " %s (not for us)\n", buf); break; default: LOGP(DPAG, LOGL_NOTICE, "Paging with unsupported MI type %d.\n", - mi_type); + mi.type); } return 0; @@ -2215,9 +3157,7 @@ static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg) chan_2 = pa->cneed2; /* first MI */ if (ms->subscr.tmsi == ntohl(pa->tmsi1) - && ms->subscr.mcc == cs->sel_mcc - && ms->subscr.mnc == cs->sel_mnc - && ms->subscr.lac == cs->sel_lac) { + && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) { LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi1)); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1, GSM_MI_TYPE_TMSI); @@ -2226,9 +3166,7 @@ static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg) ntohl(pa->tmsi1)); /* second MI */ if (ms->subscr.tmsi == ntohl(pa->tmsi2) - && ms->subscr.mcc == cs->sel_mcc - && ms->subscr.mnc == cs->sel_mnc - && ms->subscr.lac == cs->sel_lac) { + && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) { LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi2)); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1, GSM_MI_TYPE_TMSI); @@ -2285,9 +3223,7 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg) chan_4 = pa->cneed4; /* first MI */ if (ms->subscr.tmsi == ntohl(pa->tmsi1) - && ms->subscr.mcc == cs->sel_mcc - && ms->subscr.mnc == cs->sel_mnc - && ms->subscr.lac == cs->sel_lac) { + && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) { LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi1)); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1, GSM_MI_TYPE_TMSI); @@ -2296,20 +3232,16 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg) ntohl(pa->tmsi1)); /* second MI */ if (ms->subscr.tmsi == ntohl(pa->tmsi2) - && ms->subscr.mcc == cs->sel_mcc - && ms->subscr.mnc == cs->sel_mnc - && ms->subscr.lac == cs->sel_lac) { + && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) { LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi2)); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1, GSM_MI_TYPE_TMSI); } else LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n", ntohl(pa->tmsi2)); - /* thrid MI */ + /* third MI */ if (ms->subscr.tmsi == ntohl(pa->tmsi3) - && ms->subscr.mcc == cs->sel_mcc - && ms->subscr.mnc == cs->sel_mnc - && ms->subscr.lac == cs->sel_lac) { + && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) { LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi3)); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_3], 1, GSM_MI_TYPE_TMSI); @@ -2318,9 +3250,7 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg) ntohl(pa->tmsi3)); /* fourth MI */ if (ms->subscr.tmsi == ntohl(pa->tmsi4) - && ms->subscr.mcc == cs->sel_mcc - && ms->subscr.mnc == cs->sel_mnc - && ms->subscr.lac == cs->sel_lac) { + && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) { LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi4)); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_4], 1, GSM_MI_TYPE_TMSI); @@ -2335,24 +3265,23 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg) * (immediate) assignment */ -/* match request reference agains request history */ -static int gsm48_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *ref) +/* match request reference against request history */ +static int gsm48_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *ref, uint8_t hist_num) { struct gsm48_rrlayer *rr = &ms->rrlayer; int i; uint8_t ia_t1, ia_t2, ia_t3; uint8_t cr_t1, cr_t2, cr_t3; - for (i = 0; i < 3; i++) { + for (i = 0; i < hist_num; i++) { /* filter confirmed RACH requests only */ if (rr->cr_hist[i].valid && ref->ra == rr->cr_hist[i].ref.ra) { - ia_t1 = ref->t1; - ia_t2 = ref->t2; - ia_t3 = (ref->t3_high << 3) | ref->t3_low; - ref = &rr->cr_hist[i].ref; - cr_t1 = ref->t1; - cr_t2 = ref->t2; - cr_t3 = (ref->t3_high << 3) | ref->t3_low; + ia_t1 = ref->t1; + ia_t2 = ref->t2; + ia_t3 = (ref->t3_high << 3) | ref->t3_low; + cr_t1 = rr->cr_hist[i].ref.t1; + cr_t2 = rr->cr_hist[i].ref.t2; + cr_t3 = (rr->cr_hist[i].ref.t3_high << 3) | rr->cr_hist[i].ref.t3_low; if (ia_t1 == cr_t1 && ia_t2 == cr_t2 && ia_t3 == cr_t3) { LOGP(DRR, LOGL_INFO, "request %02x matches " @@ -2387,7 +3316,7 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg) /* ignore imm.ass. while not camping on a cell */ if (!cs->selected || cs->neighbour || !s) { - LOGP(DRR, LOGL_INFO, "IMMEDIATED ASSGINMENT ignored, we are " + LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT ignored, we are " "have not proper selected the serving cell.\n"); return 0; @@ -2423,7 +3352,12 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg) /* decode channel description */ LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT:\n"); cd.chan_nr = ia->chan_desc.chan_nr; - rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cd.chan_nr); + return -EINVAL; + } if (ia->chan_desc.h0.h) { cd.h = 1; gsm48_decode_chan_h1(&ia->chan_desc, &cd.tsc, &cd.maio, @@ -2437,8 +3371,7 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg) } else { cd.h = 0; gsm48_decode_chan_h0(&ia->chan_desc, &cd.tsc, &cd.arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) - cd.arfcn |= ARFCN_PCS; + cd.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd.arfcn); LOGP(DRR, LOGL_INFO, " (ta %d/%dm ra 0x%02x chan_nr 0x%02x " "ARFCN %s TS %u SS %u TSC %u)\n", ia->timing_advance, @@ -2459,7 +3392,7 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg) } /* request ref */ - if (gsm48_match_ra(ms, &ia->req_ref)) { + if (gsm48_match_ra(ms, &ia->req_ref, IMM_ASS_HISTORY)) { /* channel description */ memcpy(&rr->cd_now, &cd, sizeof(rr->cd_now)); /* timing advance */ @@ -2495,7 +3428,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) /* ignore imm.ass.ext while not camping on a cell */ if (!cs->selected || cs->neighbour || !s) { - LOGP(DRR, LOGL_INFO, "IMMEDIATED ASSGINMENT ignored, we are " + LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT ignored, we are " "have not proper selected the serving cell.\n"); return 0; @@ -2537,7 +3470,12 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) /* decode channel description */ LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT EXTENDED:\n"); cd1.chan_nr = ia->chan_desc1.chan_nr; - rsl_dec_chan_nr(cd1.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(cd1.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cd1.chan_nr); + return -EINVAL; + } if (ia->chan_desc1.h0.h) { cd1.h = 1; gsm48_decode_chan_h1(&ia->chan_desc1, &cd1.tsc, &cd1.maio, @@ -2551,8 +3489,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) } else { cd1.h = 0; gsm48_decode_chan_h0(&ia->chan_desc1, &cd1.tsc, &cd1.arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) - cd1.arfcn |= ARFCN_PCS; + cd1.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd1.arfcn); LOGP(DRR, LOGL_INFO, " assignment 1 (ta %d/%dm ra 0x%02x " "chan_nr 0x%02x ARFCN %s TS %u SS %u TSC %u)\n", ia->timing_advance1, @@ -2561,7 +3498,12 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) gsm_print_arfcn(cd1.arfcn), ch_ts, ch_subch, cd1.tsc); } cd2.chan_nr = ia->chan_desc2.chan_nr; - rsl_dec_chan_nr(cd2.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(cd2.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cd2.chan_nr); + return -EINVAL; + } if (ia->chan_desc2.h0.h) { cd2.h = 1; gsm48_decode_chan_h1(&ia->chan_desc2, &cd2.tsc, &cd2.maio, @@ -2575,8 +3517,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) } else { cd2.h = 0; gsm48_decode_chan_h0(&ia->chan_desc2, &cd2.tsc, &cd2.arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) - cd2.arfcn |= ARFCN_PCS; + cd2.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd2.arfcn); LOGP(DRR, LOGL_INFO, " assignment 2 (ta %d/%dm ra 0x%02x " "chan_nr 0x%02x ARFCN %s TS %u SS %u TSC %u)\n", ia->timing_advance2, @@ -2597,7 +3538,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) } /* request ref 1 */ - if (gsm48_match_ra(ms, &ia->req_ref1)) { + if (gsm48_match_ra(ms, &ia->req_ref1, IMM_ASS_HISTORY)) { /* channel description */ memcpy(&rr->cd_now, &cd1, sizeof(rr->cd_now)); /* timing advance */ @@ -2613,7 +3554,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) return gsm48_rr_dl_est(ms); } /* request ref 2 */ - if (gsm48_match_ra(ms, &ia->req_ref2)) { + if (gsm48_match_ra(ms, &ia->req_ref2, IMM_ASS_HISTORY)) { /* channel description */ memcpy(&rr->cd_now, &cd2, sizeof(rr->cd_now)); /* timing advance */ @@ -2663,7 +3604,7 @@ static int gsm48_rr_rx_imm_ass_rej(struct osmocom_ms *ms, struct msgb *msg) (((uint8_t *)&ia->req_ref1) + i * 4); LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT REJECT " "(ref 0x%02x)\n", req_ref->ra); - if (gsm48_match_ra(ms, req_ref)) { + if (gsm48_match_ra(ms, req_ref, IMM_ASS_HISTORY)) { /* wait indication */ t3122_value = *(((uint8_t *)&ia->wait_ind1) + i * 4); if (t3122_value) @@ -2671,7 +3612,7 @@ static int gsm48_rr_rx_imm_ass_rej(struct osmocom_ms *ms, struct msgb *msg) /* start timer 3126 if not already */ if (!osmo_timer_pending(&rr->t3126)) start_rr_t3126(rr, 5, 0); /* TODO improve! */ - /* stop assignmnet requests */ + /* stop assignment requests */ rr->n_chan_req = 0; /* wait until timer 3126 expires, then release @@ -2683,7 +3624,7 @@ static int gsm48_rr_rx_imm_ass_rej(struct osmocom_ms *ms, struct msgb *msg) return 0; } -/* 9.1.1 ADDITIONAL ASSIGMENT is received */ +/* 9.1.1 ADDITIONAL ASSIGNMENT is received */ static int gsm48_rr_rx_add_ass(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); @@ -2692,12 +3633,13 @@ static int gsm48_rr_rx_add_ass(struct osmocom_ms *ms, struct msgb *msg) struct tlv_parsed tp; if (payload_len < 0) { - LOGP(DRR, LOGL_NOTICE, "Short read of ADDITIONAL ASSIGNMENT " - "message.\n"); - return gsm48_rr_tx_rr_status(ms, - GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + LOGP(DRR, LOGL_NOTICE, "Short read of ADDITIONAL ASSIGNMENT message\n"); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, aa->data, payload_len, 0, 0) < 0) { + LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); } - tlv_parse(&tp, &gsm48_rr_att_tlvdef, aa->data, payload_len, 0, 0); return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); } @@ -2732,7 +3674,7 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms) if ((s->si5bis && s->nb_ext_ind_si5 && s->nb_ba_ind_si5bis != rep_ba) || (s->si5ter && s->nb_ba_ind_si5ter != rep_ba)) { - LOGP(DRR, LOGL_NOTICE, "BA-IND missmatch on SI5*"); + LOGP(DRR, LOGL_NOTICE, "BA-IND mismatch on SI5*"); } else rep_valid = 1; } @@ -2751,7 +3693,7 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms) if (rep_valid) { int8_t strongest, current; uint8_t ncc; - int i, index; + int i, index, strongest_i; #if 0 /* FIXME: multi-band reporting, if not: 0 = normal reporting */ @@ -2765,13 +3707,20 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms) current = -128; /* -infinite */ index = 0; for (i = 0; i < rrmeas->nc_num; i++) { + /* Skip stronger cells that have been added to measurement report so far. */ + if (rrmeas->nc_rxlev_dbm[i] > strongest) + continue; + /* Skip cells with equal strength that have been added so far. */ + if (rrmeas->nc_rxlev_dbm[i] == strongest && i <= strongest_i) + continue; /* only check if NCC is permitted */ ncc = rrmeas->nc_bsic[i] >> 3; if ((s->nb_ncc_permitted_si6 & (1 << ncc)) - && rrmeas->nc_rxlev_dbm[i] > current - && rrmeas->nc_rxlev_dbm[i] < strongest) { + && rrmeas->nc_rxlev_dbm[i] > current) { current = rrmeas->nc_rxlev_dbm[i]; + strongest = current; index = i; + strongest_i = i; } } if (current == -128) /* no more found */ @@ -2876,6 +3825,9 @@ int gsm48_rr_los(struct osmocom_ms *ms) LOGP(DSUM, LOGL_INFO, "Radio link lost signal\n"); + if (rr->vgcs.group_state != GSM48_RR_GST_OFF) + return gsm48_rr_group_rel(ms, RR_REL_CAUSE_LOST_SIGNAL); + /* stop T3211 if running */ stop_rr_t3110(rr); @@ -2980,18 +3932,22 @@ static int gsm48_rr_activate_channel(struct osmocom_ms *ms, gsm48_rr_tx_meas_rep(ms); /* establish */ - LOGP(DRR, LOGL_INFO, "establishing channel in dedicated mode\n"); - rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts); - LOGP(DRR, LOGL_INFO, " Channel type %d, subch %d, ts %d, mode %d, " - "audio-mode %d, cipher %d\n", ch_type, ch_subch, ch_ts, - cd->mode, rr->audio_mode, rr->cipher_type + 1); + LOGP(DRR, LOGL_INFO, "establishing channel in dedicated/group mode\n"); + + if (rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cd->chan_nr); + return -EINVAL; + } + + LOGP(DRR, LOGL_INFO, " Channel type %d, subch %d, ts %d, mode %d, audio-mode %d, flags 0x%02x, cipher %d\n", + ch_type, ch_subch, ch_ts, cd->mode, rr->audio_mode, cd->tch_flags, rr->cipher_type + 1); if (cd->h) - l1ctl_tx_dm_est_req_h1(ms, cd->maio, cd->hsn, - ma, ma_len, cd->chan_nr, cd->tsc, cd->mode, - rr->audio_mode); + l1ctl_tx_dm_est_req_h1(ms, cd->maio, cd->hsn, ma, ma_len, cd->chan_nr, cd->tsc, cd->mode, + rr->audio_mode, cd->tch_flags); else - l1ctl_tx_dm_est_req_h0(ms, cd->arfcn, cd->chan_nr, cd->tsc, - cd->mode, rr->audio_mode); + l1ctl_tx_dm_est_req_h0(ms, cd->arfcn, cd->chan_nr, cd->tsc, cd->mode, rr->audio_mode, cd->tch_flags); rr->dm_est = 1; /* old SI 5/6 are not valid on a new dedicated channel */ @@ -3021,7 +3977,7 @@ static int gsm48_rr_channel_after_time(struct osmocom_ms *ms, l1ctl_tx_crypto_req(ms, rr->cd_now.chan_nr, rr->cipher_type + 1, subscr->key, 8); - gsm48_rr_set_mode(ms, cd->chan_nr, cd->mode); + gsm48_rr_set_mode(ms, cd->chan_nr, cd->mode, cd->tch_flags); return 0; } @@ -3033,8 +3989,8 @@ static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = cs->si; struct gsm_settings *set = &ms->settings; - int i, pcs, index; - uint16_t arfcn; + int i, index; + uint16_t arfcn, pcs; pcs = gsm_refer_pcs(cs->arfcn, s) ? ARFCN_PCS : 0; @@ -3054,7 +4010,7 @@ static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, LOGP(DRR, LOGL_INFO, "using cell channel descr.\n"); if (cd->cell_desc_lv[0] != 16) { LOGP(DRR, LOGL_ERROR, "cell channel descr. " - "has invalid lenght\n"); + "has invalid length\n"); return GSM48_RR_CAUSE_ABNORMAL_UNSPEC; } gsm48_decode_freq_list(freq, cd->cell_desc_lv + 1, 16, @@ -3137,7 +4093,9 @@ static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, /* convert to band_arfcn and check for unsported frequency */ for (i = 0; i < *ma_len; i++) { - arfcn = ma[i] | pcs; + arfcn = ma[i]; + if (arfcn >= 512 && arfcn <= 810) + arfcn |= pcs; ma[i] = arfcn; index = arfcn2index(arfcn); if (!(set->freq_map[index >> 3] & (1 << (index & 7)))) { @@ -3154,12 +4112,11 @@ static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, static int gsm48_rr_dl_est(struct osmocom_ms *ms) { struct gsm48_rrlayer *rr = &ms->rrlayer; - struct gsm322_cellsel *cs = &ms->cellsel; struct gsm_subscriber *subscr = &ms->subscr; + struct gsm322_cellsel *cs = &ms->cellsel; struct msgb *nmsg; struct gsm48_hdr *gh; struct gsm48_pag_rsp *pr; - uint8_t mi[11]; uint16_t ma[64]; uint8_t ma_len; @@ -3233,26 +4190,21 @@ static int gsm48_rr_dl_est(struct osmocom_ms *ms) pr->cm2_len = sizeof(pr->cm2); gsm48_rr_enc_cm2(ms, &pr->cm2, rr->cd_now.arfcn); /* mobile identity */ - if (ms->subscr.tmsi != 0xffffffff - && ms->subscr.mcc == cs->sel_mcc - && ms->subscr.mnc == cs->sel_mnc - && ms->subscr.lac == cs->sel_lac + if (ms->subscr.tmsi != GSM_RESERVED_TMSI + && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0) && rr->paging_mi_type == GSM_MI_TYPE_TMSI) { - gsm48_generate_mid_from_tmsi(mi, subscr->tmsi); + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_TMSI, false); LOGP(DRR, LOGL_INFO, "sending paging response with " "TMSI\n"); } else if (subscr->imsi[0]) { - gsm48_generate_mid_from_imsi(mi, subscr->imsi); + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, false); LOGP(DRR, LOGL_INFO, "sending paging response with " "IMSI\n"); } else { - mi[1] = 1; - mi[2] = 0xf0 | GSM_MI_TYPE_NONE; + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_NONE, false); LOGP(DRR, LOGL_INFO, "sending paging response without " "TMSI/IMSI\n"); } - msgb_put(nmsg, 1 + mi[1]); - memcpy(pr->data, mi + 1, 1 + mi[1]); } #ifdef TEST_FREQUENCY_MOD @@ -3326,6 +4278,12 @@ static int gsm48_rr_rel_ind(struct osmocom_ms *ms, struct msgb *msg) struct msgb *nmsg; struct gsm48_rr_hdr *nrrh; + /* Handle on group channel. */ + if (rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) { + LOGP(DRR, LOGL_INFO, "Returning to group receive mode, due to link release indication.\n"); + return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_NORMAL); + } + /* switch back to old channel, if modify/ho failed */ switch (rr->modify_state) { case GSM48_RR_MOD_ASSIGN: @@ -3372,12 +4330,13 @@ static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg) 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); + LOGP(DRR, LOGL_NOTICE, "Short read of CHANNEL RELEASE message\n"); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, cr->data, payload_len, 0, 0) < 0) { + LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + 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); @@ -3395,6 +4354,9 @@ static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg) new_rr_state(rr, GSM48_RR_ST_REL_PEND); + /* When we receive a channel release, we are not just releasing the transmit mode. */ + rr->vgcs.group_state = GSM48_RR_GST_OFF; + /* start T3110, so that two DISCs can be sent due to T200 timeout */ start_rr_t3110(rr, 1, 500000); @@ -3412,27 +4374,59 @@ static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg) return 0; } +/* Release of channel on VGCS/VBS. */ +static int gsm48_rr_rx_chan_rel_ui(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; + + 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); + } + if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, cr->data, payload_len, 0, 0) < 0) { + LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + + LOGP(DRR, LOGL_INFO, "CHANNEL RELESE via UI frame with cause 0x%02x\n", cr->rr_cause); + + /* Only allow when in group receive mode. */ + if (rr->vgcs.group_state != GSM48_RR_GST_RECEIVE) + return 0; + + return gsm48_rr_group_rel(ms, RR_REL_CAUSE_NORMAL); +} + /* - * frequency redefition, chanel mode modify, assignment, and handover + * frequency redefition, channel mode modify, assignment, and handover */ /* set channel mode in case of TCH */ -static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr, - uint8_t mode) +static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr, uint8_t mode, uint8_t tch_flags) { struct gsm48_rrlayer *rr = &ms->rrlayer; uint8_t ch_type, ch_subch, ch_ts; + if (rsl_dec_chan_nr(chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, chan_nr); + return -EINVAL; + } + /* only apply mode to TCH/F or TCH/H */ - rsl_dec_chan_nr(chan_nr, &ch_type, &ch_subch, &ch_ts); if (ch_type != RSL_CHAN_Bm_ACCHs && ch_type != RSL_CHAN_Lm_ACCHs) return -ENOTSUP; - /* setting (new) timing advance */ - LOGP(DRR, LOGL_INFO, "setting TCH mode to %d, audio mode to %d\n", - mode, rr->audio_mode); - l1ctl_tx_tch_mode_req(ms, mode, rr->audio_mode); + /* Apply indicated channel mode */ + LOGP(DRR, LOGL_INFO, "setting TCH mode to %s, audio mode to %d, tch flags to %d\n", + get_value_string(gsm48_chan_mode_names, mode), rr->audio_mode, tch_flags); + l1ctl_tx_tch_mode_req(ms, mode, rr->audio_mode, tch_flags, rr->tch_loop_mode); return 0; } @@ -3469,7 +4463,12 @@ static int gsm48_rr_rx_frq_redef(struct osmocom_ms *ms, struct msgb *msg) /* decode channel description */ LOGP(DRR, LOGL_INFO, "FREQUENCY REDEFINITION:\n"); cd.chan_nr = fr->chan_desc.chan_nr; - rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cd.chan_nr); + return -EINVAL; + } if (fr->chan_desc.h0.h) { cd.h = 1; gsm48_decode_chan_h1(&fr->chan_desc, &cd.tsc, &cd.maio, @@ -3479,8 +4478,7 @@ static int gsm48_rr_rx_frq_redef(struct osmocom_ms *ms, struct msgb *msg) } else { cd.h = 0; gsm48_decode_chan_h0(&fr->chan_desc, &cd.tsc, &cd.arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) - cd.arfcn |= ARFCN_PCS; + cd.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd.arfcn); LOGP(DRR, LOGL_INFO, " (ARFCN %s TS %u SS %u TSC %u)\n", gsm_print_arfcn(cd.arfcn), ch_ts, ch_subch, cd.tsc); } @@ -3559,7 +4557,7 @@ static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg) int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cm); struct gsm48_rr_cd *cd = &rr->cd_now; uint8_t ch_type, ch_subch, ch_ts; - uint8_t cause; + int rc; LOGP(DRR, LOGL_INFO, "CHANNEL MODE MODIFY\n"); @@ -3573,7 +4571,12 @@ static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg) /* decode channel description */ cd->chan_nr = cm->chan_desc.chan_nr; - rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cd->chan_nr); + return -EINVAL; + } if (cm->chan_desc.h0.h) { cd->h = 1; gsm48_decode_chan_h1(&cm->chan_desc, &cd->tsc, &cd->maio, @@ -3584,21 +4587,36 @@ static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg) } else { cd->h = 0; gsm48_decode_chan_h0(&cm->chan_desc, &cd->tsc, &cd->arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) - cd->arfcn |= ARFCN_PCS; + cd->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd->arfcn); LOGP(DRR, LOGL_INFO, " (chan_nr 0x%02x ARFCN %s TS %u SS %u " "TSC %u mode %u)\n", cm->chan_desc.chan_nr, gsm_print_arfcn(cd->arfcn), ch_ts, ch_subch, cd->tsc, cm->mode); } - /* mode */ - cause = gsm48_rr_check_mode(ms, cd->chan_nr, cm->mode); - if (cause) - return gsm48_rr_tx_rr_status(ms, cause); + + /** + * According to 3GPP TS 04.08, section 3.4.6.1.3 + * "Abnormal cases" of "channel mode modify procedure", + * if the MS doesn't support the indicated channel mode, + * it shall retain the old mode and return the associated + * channel mode information in the ACKNOWLEDGE message. + */ + + /* Check if we support this channel mode */ + rc = gsm48_rr_check_mode(ms, cd->chan_nr, cm->mode); + if (rc) + goto ack; + + /* Attempt to apply this mode */ + rc = gsm48_rr_set_mode(ms, cd->chan_nr, cm->mode, cd->tch_flags); + if (rc) + goto ack; + + /* Finally set (a new) mode */ cd->mode = cm->mode; - gsm48_rr_set_mode(ms, cd->chan_nr, cd->mode); - return gsm48_rr_tx_chan_modify_ack(ms, &cm->chan_desc, cm->mode); +ack: + return gsm48_rr_tx_chan_modify_ack(ms, &cm->chan_desc, cd->mode); } /* 9.1.3 sending ASSIGNMENT COMPLETE */ @@ -3684,19 +4702,25 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg) cdb->ind_tx_power = rr->cd_now.ind_tx_power; if (payload_len < 0) { - LOGP(DRR, LOGL_NOTICE, "Short read of ASSIGNMENT COMMAND " - "message.\n"); - return gsm48_rr_tx_rr_status(ms, - GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + LOGP(DRR, LOGL_NOTICE, "Short read of ASSIGNMENT COMMAND message\n"); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, ac->data, payload_len, 0, 0) < 0) { + LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); } - tlv_parse(&tp, &gsm48_rr_att_tlvdef, ac->data, payload_len, 0, 0); /* decode channel description (before time) */ if (TLVP_PRESENT(&tp, GSM48_IE_CH_DESC_1_BEFORE)) { struct gsm48_chan_desc *ccd = (struct gsm48_chan_desc *) TLVP_VAL(&tp, GSM48_IE_CH_DESC_1_BEFORE); cdb->chan_nr = ccd->chan_nr; - rsl_dec_chan_nr(cdb->chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(cdb->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cdb->chan_nr); + return -EINVAL; + } if (ccd->h0.h) { cdb->h = 1; gsm48_decode_chan_h1(ccd, &cdb->tsc, &cdb->maio, @@ -3707,8 +4731,7 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg) } else { cdb->h = 0; gsm48_decode_chan_h0(ccd, &cdb->tsc, &cdb->arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) - cdb->arfcn |= ARFCN_PCS; + cdb->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cdb->arfcn); LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x " "ARFCN %s TS %u SS %u TSC %u)\n", ccd->chan_nr, gsm_print_arfcn(cdb->arfcn), @@ -3719,7 +4742,12 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg) /* decode channel description (after time) */ cda->chan_nr = ac->chan_desc.chan_nr; - rsl_dec_chan_nr(cda->chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(cda->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cda->chan_nr); + return -EINVAL; + } if (ac->chan_desc.h0.h) { cda->h = 1; gsm48_decode_chan_h1(&ac->chan_desc, &cda->tsc, &cda->maio, @@ -3730,8 +4758,7 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg) } else { cda->h = 0; gsm48_decode_chan_h0(&ac->chan_desc, &cda->tsc, &cda->arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) - cda->arfcn |= ARFCN_PCS; + cda->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cda->arfcn); LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x ARFCN %s TS %u " "SS %u TSC %u)\n", ac->chan_desc.chan_nr, gsm_print_arfcn(cda->arfcn), ch_ts, ch_subch, cda->tsc); @@ -4057,10 +5084,12 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) cdb->ind_tx_power = rr->cd_now.ind_tx_power; if (payload_len < 0) { - LOGP(DRR, LOGL_NOTICE, "Short read of HANDOVER COMMAND " - "message.\n"); - return gsm48_rr_tx_rr_status(ms, - GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + LOGP(DRR, LOGL_NOTICE, "Short read of HANDOVER COMMAND message\n"); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, ho->data, payload_len, 0, 0) < 0) { + LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); } /* cell description */ @@ -4070,8 +5099,6 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) rr->chan_req_val = ho->ho_ref; rr->chan_req_mask = 0x00; - tlv_parse(&tp, &gsm48_rr_att_tlvdef, ho->data, payload_len, 0, 0); - /* sync ind */ if (TLVP_PRESENT(&tp, GSM48_IE_SYNC_IND)) { gsm48_decode_sync_ind(rr, (struct gsm48_sync_ind *) @@ -4085,7 +5112,12 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) struct gsm48_chan_desc *ccd = (struct gsm48_chan_desc *) TLVP_VAL(&tp, GSM48_IE_CH_DESC_1_BEFORE); cdb->chan_nr = ccd->chan_nr; - rsl_dec_chan_nr(cdb->chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(cdb->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cdb->chan_nr); + return -EINVAL; + } if (ccd->h0.h) { cdb->h = 1; gsm48_decode_chan_h1(ccd, &cdb->tsc, &cdb->maio, @@ -4096,8 +5128,7 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) } else { cdb->h = 0; gsm48_decode_chan_h0(ccd, &cdb->tsc, &cdb->arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) - cdb->arfcn |= ARFCN_PCS; + cdb->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cdb->arfcn); LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x " "ARFCN %s TS %u SS %u TSC %u)\n", ccd->chan_nr, gsm_print_arfcn(cdb->arfcn), @@ -4108,7 +5139,12 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) /* decode channel description (after time) */ cda->chan_nr = ho->chan_desc.chan_nr; - rsl_dec_chan_nr(cda->chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(cda->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cda->chan_nr); + return -EINVAL; + } if (ho->chan_desc.h0.h) { cda->h = 1; gsm48_decode_chan_h1(&ho->chan_desc, &cda->tsc, &cda->maio, @@ -4119,8 +5155,7 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) } else { cda->h = 0; gsm48_decode_chan_h0(&ho->chan_desc, &cda->tsc, &cda->arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) - cda->arfcn |= ARFCN_PCS; + cda->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cda->arfcn); LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x ARFCN %s TS %u " "SS %u TSC %u)\n", ho->chan_desc.chan_nr, gsm_print_arfcn(cda->arfcn), ch_ts, ch_subch, cda->tsc); @@ -4366,6 +5401,16 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) return 0; } +/* send HANDOVER/UPLINK ACCESS burst (9.1.14) */ +static int gsm48_rr_tx_rand_acc_dedicated(struct osmocom_ms *ms, uint8_t ref, uint16_t offset, int8_t uic) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + LOGP(DRR, LOGL_INFO, "send access burst on DCCH (ref 0x%02x)\n", ref); + + return l1ctl_tx_rach_req(ms, rr->cd_now.chan_nr, 0x00, ref, 0, offset, uic); +} + /* send all queued messages down to layer 2 */ static int gsm48_rr_dequeue_down(struct osmocom_ms *ms) { @@ -4388,17 +5433,27 @@ static int gsm48_rr_dequeue_down(struct osmocom_ms *ms) return 0; } -/* channel is resumed in dedicated mode */ +/* Channel is resumed in dedicated mode or uplink is estabished. */ static int gsm48_rr_estab_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; - LOGP(DRR, LOGL_INFO, "data link is resumed\n"); + if (rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) { + LOGP(DRR, LOGL_INFO, "data link established on uplink\n"); - /* transmit queued frames during ho / ass transition */ - gsm48_rr_dequeue_down(ms); + /* Confirm uplink access. */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_UPLINK_CNF); + if (nmsg) + gsm48_rr_upmsg(ms, nmsg); + } else { + LOGP(DRR, LOGL_INFO, "data link is resumed\n"); - rr->modify_state = GSM48_RR_MOD_NONE; + /* transmit queued frames during ho / ass transition */ + gsm48_rr_dequeue_down(ms); + + rr->modify_state = GSM48_RR_MOD_NONE; + } return 0; } @@ -4488,6 +5543,13 @@ static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg) struct gsm48_rr_hdr *nrrh; uint16_t acc_class; + /* Reject during group call. */ + if (rr->vgcs.group_state != GSM48_RR_GST_OFF) { + LOGP(DRR, LOGL_INFO, "We have a group call, rejecting!\n"); + cause = RR_REL_CAUSE_TRY_LATER; + goto reject; + } + /* 3.3.1.1.3.2 */ if (osmo_timer_pending(&rr->t3122)) { if (rrh->cause != RR_EST_CAUSE_EMERGENCY) { @@ -4537,7 +5599,7 @@ static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg) goto reject; } - /* check for relevant informations */ + /* check for relevant information */ if (!s->si3) { LOGP(DRR, LOGL_INFO, "Not enough SI, rejecting!\n"); cause = RR_REL_CAUSE_TRY_LATER; @@ -4575,10 +5637,317 @@ static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg) memcpy(msgb_put(rr->rr_est_msg, msgb_l3len(msg)), msgb_l3(msg), msgb_l3len(msg)); + if (rr->vgcs.group_state == GSM48_RR_GST_RECEIVE) { + /* Release group receive mode. */ + l1ctl_tx_dm_rel_req(rr->ms); + rr->ms->meas.rl_fail = 0; + l1ctl_tx_reset_req(rr->ms, L1CTL_RES_T_SCHED); + } + /* request channel */ return gsm48_rr_chan_req(ms, rrh->cause, 0, 0); } +/* 3.3.3.2 Request for group receive mode. */ +static int gsm48_rr_group_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = &cs->sel_si; + struct gsm48_chan_desc *ch_desc = (struct gsm48_chan_desc *)(msg->data + sizeof(struct gsm48_rr_hdr)); + uint8_t cause; + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + struct gsm48_rr_cd cd; + uint8_t ch_type, ch_subch, ch_ts; + uint16_t ma[64]; + uint8_t ma_len; + int rc; + + /* if state is not idle */ + if (rr->state != GSM48_RR_ST_IDLE || rr->vgcs.group_state != GSM48_RR_GST_OFF) { + LOGP(DRR, LOGL_INFO, "We are not IDLE yet, rejecting!\n"); + cause = RR_REL_CAUSE_TRY_LATER; +reject: + LOGP(DSUM, LOGL_INFO, "Joining group call channel not possible\n"); + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_GROUP_REL_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = cause; + return gsm48_rr_upmsg(ms, nmsg); + } + + /* cell selected */ + if (!cs->selected) { + LOGP(DRR, LOGL_INFO, "No cell selected, rejecting!\n"); + cause = RR_REL_CAUSE_TRY_LATER; + goto reject; + } + + /* check if camping */ + if (cs->state != GSM322_C3_CAMPED_NORMALLY + && cs->state != GSM322_C7_CAMPED_ANY_CELL) { + LOGP(DRR, LOGL_INFO, "Not camping, rejecting! " + "(cs->state = %d)\n", cs->state); + cause = RR_REL_CAUSE_TRY_LATER; + goto reject; + } + + /* get channel description */ + memset(&cd, 0, sizeof(cd)); + cd.chan_nr = ch_desc->chan_nr; + if (rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cd.chan_nr); + cause = GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + goto reject; + } + if (ch_desc->h0.h) { + LOGP(DRR, LOGL_ERROR, "HOPPING NOT SUPPORTED, PLEASE FIX!\n"); + cd.h = 1; + gsm48_decode_chan_h1(ch_desc, &cd.tsc, &cd.maio, + &cd.hsn); + LOGP(DRR, LOGL_INFO, " (chan_nr 0x%02x MAIO %u HSN %u TS %u SS %u TSC %u)\n", + ch_desc->chan_nr, cd.maio, cd.hsn, ch_ts, ch_subch, cd.tsc); + } else { + cd.h = 0; + gsm48_decode_chan_h0(ch_desc, &cd.tsc, &cd.arfcn); + cd.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd.arfcn); + LOGP(DRR, LOGL_INFO, " (chan_nr 0x%02x ARFCN %s TS %u SS %u TSC %u)\n", + ch_desc->chan_nr, gsm_print_arfcn(cd.arfcn), ch_ts, ch_subch, cd.tsc); + } + if (gsm48_rr_render_ma(ms, &rr->vgcs.cd_group, ma, &ma_len)) { + cause = GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + goto reject; + } + + /* Turn off transmitter. */ + cd.tch_flags |= L1CTL_TCH_FLAG_RXONLY; + + /* Set mode to Speech V1. FIXME: Add AMR support. */ + cd.mode = GSM48_CMODE_SPEECH_V1; + + /* Set current channel and also store as 'vgcs.cd_group', used when leaving dedicated mode. */ + memcpy(&rr->cd_now, &cd, sizeof(rr->cd_now)); + memcpy(&rr->vgcs.cd_group, &cd, sizeof(rr->vgcs.cd_group)); + + /* tell cell selection process to leave idle mode + * NOTE: this must be sent unbuffered, because the state may not + * change until idle mode is left + */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_LEAVE_IDLE); + if (!nmsg) + return -ENOMEM; + rc = gsm322_c_event(ms, nmsg); + msgb_free(nmsg); + if (rc) { + LOGP(DRR, LOGL_INFO, "Failed to leave IDLE mode.\n"); + cause = GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + goto reject; + } + + /* Initial uplink state. */ + rr->vgcs.uplink_free = false; + + /* Set group state to receive mode. */ + rr->vgcs.group_state = GSM48_RR_GST_RECEIVE; + + /* Wait until synced to the CCCH ... */ + return 0; +} +/* ... Continue after synced to the CCCH. */ +static int gsm48_rr_group_req_continue(struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + uint16_t ma[64]; + uint8_t ma_len; + struct msgb *nmsg; + + /* get hopping sequence, if required */ + gsm48_rr_render_ma(ms, &rr->vgcs.cd_group, ma, &ma_len); + /* activate channel */ + gsm48_rr_activate_channel(ms, &rr->vgcs.cd_group, ma, ma_len); + + /* Confirm group call channel. */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_GROUP_CNF); + if (!nmsg) + return -ENOMEM; + return gsm48_rr_upmsg(ms, nmsg); +} + +/* After "loss of signal"/release in group transmit or receive mode, return IDLE and release towards MM. */ +static int gsm48_rr_group_rel(struct osmocom_ms *ms, int cause) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + uint8_t *mode; + + /* Stop group receive and transmit timers. */ + stop_rr_t_ul_free(rr); + stop_rr_t3128(rr); + stop_rr_t3130(rr); + + if (rr->state == GSM48_RR_ST_DEDICATED || rr->state == GSM48_RR_ST_REL_PEND) { + struct msgb *nmsg; + + LOGP(DRR, LOGL_INFO, "Channel lost, send (local) release to layer 2.\n"); + + /* Go into group receive mode, so that the channel gets released on LAPD release confirm. */ + rr->vgcs.group_state = GSM48_RR_GST_RECEIVE; + if (rr->state != GSM48_RR_ST_REL_PEND) + 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] = RSL_REL_LOCAL_END; + gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0); + + /* release SAPI 3 link, if exits */ + gsm48_release_sapi3_link(ms); + + /* Wait for LAPD to confirm. Then return idle. */ + return 0; + } + + /* Go back to IDLE mode. */ + rr->vgcs.group_state = GSM48_RR_GST_OFF; + new_rr_state(rr, GSM48_RR_ST_IDLE); + + /* Indicate release to MM. */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_GROUP_REL_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = cause; + return gsm48_rr_upmsg(ms, nmsg); +} + +/* Release group channel, return to IDLE. */ +static int gsm48_rr_group_rel_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + /* Only in group receive/transmit mode. */ + if (rr->vgcs.group_state == GSM48_RR_GST_OFF) + return 0; + + if (rr->state == GSM48_RR_ST_CONN_PEND || rr->state == GSM48_RR_ST_DEDICATED) { + LOGP(DRR, LOGL_INFO, "Connot release group channel yet, perform uplink release first.\n"); + gsm48_rr_uplink_rel_req(ms, msg); + if (rr->state == GSM48_RR_ST_REL_PEND) { + /* Leave the group call state, so that we change to IDLE when we released the uplink. */ + rr->vgcs.group_state = GSM48_RR_GST_OFF; + return 0; + } + } + + /* Stop group receive and transmit timers. */ + stop_rr_t_ul_free(rr); + stop_rr_t3128(rr); + stop_rr_t3130(rr); + + /* Unset group state. Wait, if release is pending. */ + rr->vgcs.group_state = GSM48_RR_GST_OFF; + if (rr->state != GSM48_RR_ST_IDLE) { + LOGP(DRR, LOGL_INFO, "Cannot release group channel yet, wait to return to IDLE state.\n"); + return 0; + } + + LOGP(DRR, LOGL_INFO, "Release Group channel.\n"); + new_rr_state(rr, GSM48_RR_ST_IDLE); + return 0; +} + +/* Request uplink in group receive mode. */ +static int gsm48_rr_uplink_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + + /* Only in group receive mode */ + if (rr->state != GSM48_RR_ST_IDLE || rr->vgcs.group_state != GSM48_RR_GST_RECEIVE) { + LOGP(DRR, LOGL_INFO, "We are not in group receive mode yet, rejecting!\n"); + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_UPLINK_REL_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = RR_REL_CAUSE_TRY_LATER; + return gsm48_rr_upmsg(ms, nmsg); + } + + LOGP(DRR, LOGL_INFO, "Changing from group receive mode to group transmit mode.\n"); + + /* Enter uplink access procedure. */ + new_rr_state(rr, GSM48_RR_ST_CONN_PEND); + + /* Set group state to transmit mode. */ + rr->vgcs.group_state = GSM48_RR_GST_TRANSMIT; + + /* Uplink investigation procedure (3.3.1.2.1.1) */ + if (!rr->vgcs.uplink_free) { + start_rr_t3128(rr, GSM_T3128_MS); + return 0; + } + + rr->vgcs.uplink_tries = 3; + return gsm48_rr_uplink_access(ms, NULL); +} + +/* Leave uplink, also called when leaving group channel. */ +static int gsm48_rr_uplink_rel_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm_settings *set = &ms->settings; + struct msgb *nmsg; + uint8_t *mode; + + /* Only in group transmit mode */ + if (rr->vgcs.group_state != GSM48_RR_GST_TRANSMIT) + return -EINVAL; + + /* Stop group transmit mode timers. */ + stop_rr_t3128(rr); + stop_rr_t3130(rr); + + /* Continue if in dedicated mode, so we release and wait for uplink to become free. */ + if (rr->state != GSM48_RR_ST_DEDICATED) + return 0; + + LOGP(DRR, LOGL_INFO, "Returning from group transmit to group receive mode.\n"); + + /* Leave uplink, wait for uplink being free, channel release or channel/link failure. */ + gsm48_rr_tx_uplink_release(ms, GSM48_RR_CAUSE_LEAVE_GROUP_CA); + + /* Go into release pending mode. */ + new_rr_state(rr, GSM48_RR_ST_REL_PEND); + + /* Special setting where we release locally. This means we wait for free uplink an then release. */ + if (set->uplink_release_local) { + LOGP(DRR, LOGL_INFO, "Release L2 locally as specified via VTY. Wait for UPLINK FREE.\n"); + return 0; + } + + /* Release dedicated mode. */ + /* 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] = RSL_REL_NORMAL; + gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0); + + return 0; +} + /* 3.4.2 transfer data in dedicated mode */ static int gsm48_rr_data_req(struct osmocom_ms *ms, struct msgb *msg) { @@ -4623,17 +5992,21 @@ static int gsm48_rr_data_req(struct osmocom_ms *ms, struct msgb *msg) /* 3.4.2 data from layer 2 to RR and upper layer*/ static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg) { + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + uint8_t sapi = rllh->link_id & 7; struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_rr_hdr *rrh; uint8_t pdisc = gh->proto_discr & 0x0f; + int rc = -EINVAL; if (pdisc == GSM48_PDISC_RR) { - int rc = -EINVAL; uint8_t skip_ind = (gh->proto_discr & 0xf0) >> 4; /* ignore if skip indicator is not B'0000' */ - if (skip_ind) + if (skip_ind) { + msgb_free(msg); return 0; + } switch(gh->msg_type) { case GSM48_MT_RR_ADD_ASS: @@ -4660,6 +6033,9 @@ static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg) case GSM48_MT_RR_CHAN_REL: rc = gsm48_rr_rx_chan_rel(ms, msg); break; + case GSM48_MT_RR_UPLINK_RELEASE: + rc = gsm48_rr_rx_uplink_release(ms, msg); + break; case GSM48_MT_RR_APP_INFO: LOGP(DRR, LOGL_NOTICE, "APP INFO not supported!\n"); break; @@ -4673,6 +6049,10 @@ static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg) msgb_free(msg); return rc; + } else if (pdisc == GSM48_PDISC_TEST) { + rc = gsm414_rcv_test(ms, msg); + msgb_free(msg); + return rc; } /* pull off RSL header up to L3 message */ @@ -4682,6 +6062,7 @@ static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg) msgb_push(msg, sizeof(struct gsm48_rr_hdr)); rrh = (struct gsm48_rr_hdr *)msg->data; rrh->msg_type = GSM48_RR_DATA_IND; + rrh->sapi = sapi; return gsm48_rr_upmsg(ms, msg); } @@ -4704,6 +6085,8 @@ static int gsm48_rr_rx_bcch(struct osmocom_ms *ms, struct msgb *msg) return gsm48_rr_rx_sysinfo3(ms, msg); case GSM48_MT_RR_SYSINFO_4: return gsm48_rr_rx_sysinfo4(ms, msg); + case GSM48_MT_RR_SYSINFO_13: + return gsm48_rr_rx_sysinfo13(ms, msg); default: #if 0 LOGP(DRR, LOGL_NOTICE, "BCCH message type 0x%02x not sup.\n", @@ -4718,6 +6101,11 @@ static int gsm48_rr_rx_pch_agch(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_system_information_type_header *sih = msgb_l3(msg); + if (msgb_l3len(msg) < sizeof(*sih)) { + LOGP(DRR, LOGL_NOTICE, "Short read of CCCH message.\n"); + return -EINVAL; + } + switch (sih->system_information) { case GSM48_MT_RR_PAG_REQ_1: return gsm48_rr_rx_pag_req_1(ms, msg); @@ -4732,6 +6120,10 @@ static int gsm48_rr_rx_pch_agch(struct osmocom_ms *ms, struct msgb *msg) return gsm48_rr_rx_imm_ass_ext(ms, msg); case GSM48_MT_RR_IMM_ASS_REJ: return gsm48_rr_rx_imm_ass_rej(ms, msg); + + case GSM48_MT_RR_NOTIF_NCH: + return gsm48_rr_rx_notif_nch(ms, msg); + default: #if 0 LOGP(DRR, LOGL_NOTICE, "CCCH message type 0x%02x unknown.\n", @@ -4741,48 +6133,70 @@ static int gsm48_rr_rx_pch_agch(struct osmocom_ms *ms, struct msgb *msg) } } +#define N201_Bter_SACCH 21 +#define N201_Bter_SDCCH_FACCH 23 +#define N201_B4 19 + /* receive ACCH at RR layer */ static int gsm48_rr_rx_acch(struct osmocom_ms *ms, struct msgb *msg) { - struct gsm48_rrlayer *rr = &ms->rrlayer; - struct gsm_settings *set = &ms->settings; - struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); - struct gsm48_hdr *sih = msgb_l3(msg); - uint8_t ind_ta, ind_tx_power; - - if (msgb_l2len(msg) < sizeof(*rllh) + 2 + 2) { - LOGP(DRR, LOGL_ERROR, "Missing TA and TX_POWER IEs\n"); - return -EINVAL; + const struct gsm48_system_information_type_header *sih; + const struct gsm48_hdr_sh *sgh; + const struct gsm48_hdr *gh; + + /* Bter frame (SACCH or SDCCH/FACCH) */ + if (msgb_l3len(msg) == N201_Bter_SACCH || msgb_l3len(msg) == N201_Bter_SDCCH_FACCH) { + sgh = msgb_l3(msg); + if (sgh->rr_short_pd != GSM48_PDISC_SH_RR) { + LOGP(DRR, LOGL_NOTICE, "Short header message is not an RR message.\n"); + return -EINVAL; + } + switch (sgh->msg_type) { + case GSM48_MT_RR_SH_UL_FREE: + return gsm48_rr_rx_uplink_free(ms, msg); + case GSM48_MT_RR_SH_FACCH: + return gsm48_rr_rx_notif_facch(ms, msg); + case GSM48_MT_RR_SH_SI10: + return gsm48_rr_rx_sysinfo_10(ms, msg); + default: + LOGP(DRR, LOGL_NOTICE, "Short header message type 0x%02x unsupported.\n", sgh->msg_type); + return -EINVAL; + } } - ind_ta = rllh->data[1]; - ind_tx_power = rllh->data[3]; - LOGP(DRR, LOGL_INFO, "Indicated ta %d (actual ta %d)\n", - ind_ta, ind_ta - set->alter_delay); - LOGP(DRR, LOGL_INFO, "Indicated tx_power %d\n", - ind_tx_power); - if (ind_ta != rr->cd_now.ind_ta - || ind_tx_power != rr->cd_now.ind_tx_power) { - LOGP(DRR, LOGL_INFO, "setting new ta and tx_power\n"); - l1ctl_tx_param_req(ms, ind_ta - set->alter_delay, - (set->alter_tx_power) ? set->alter_tx_power_value - : ind_tx_power); - rr->cd_now.ind_ta = ind_ta; - rr->cd_now.ind_tx_power = ind_tx_power; + /* B4 frame (SACCH) */ + if ((msg->cb[0] & 0x40) && msgb_l3len(msg) == N201_B4) { + sih = msgb_l3(msg); + switch (sih->system_information) { + case GSM48_MT_RR_SYSINFO_5: + return gsm48_rr_rx_sysinfo5(ms, msg); + case GSM48_MT_RR_SYSINFO_5bis: + return gsm48_rr_rx_sysinfo5bis(ms, msg); + case GSM48_MT_RR_SYSINFO_5ter: + return gsm48_rr_rx_sysinfo5ter(ms, msg); + case GSM48_MT_RR_SYSINFO_6: + return gsm48_rr_rx_sysinfo6(ms, msg); + default: + LOGP(DRR, LOGL_NOTICE, "ACCH message type 0x%02x unknown.\n", sih->system_information); + return -EINVAL; + } } - switch (sih->msg_type) { - case GSM48_MT_RR_SYSINFO_5: - return gsm48_rr_rx_sysinfo5(ms, msg); - case GSM48_MT_RR_SYSINFO_5bis: - return gsm48_rr_rx_sysinfo5bis(ms, msg); - case GSM48_MT_RR_SYSINFO_5ter: - return gsm48_rr_rx_sysinfo5ter(ms, msg); - case GSM48_MT_RR_SYSINFO_6: - return gsm48_rr_rx_sysinfo6(ms, msg); + /* B frame (UI frame on VGCS) */ + if (msgb_l3len(msg) < sizeof(*gh)) { + LOGP(DRR, LOGL_NOTICE, "ACCH message too short.\n"); + return -EINVAL; + } + gh = msgb_l3(msg); + switch (gh->msg_type) { + case GSM48_MT_RR_VGCS_UPL_GRANT: + return gsm48_rr_rx_vgcs_uplink_grant(ms, msg); + case GSM48_MT_RR_UPLINK_BUSY: + return gsm48_rr_rx_uplink_busy(ms, msg); + case GSM48_MT_RR_CHAN_REL: + return gsm48_rr_rx_chan_rel_ui(ms, msg); default: - LOGP(DRR, LOGL_NOTICE, "ACCH message type 0x%02x unknown.\n", - sih->msg_type); + LOGP(DRR, LOGL_NOTICE, "ACCH message type 0x%02x unknown.\n", gh->msg_type); return -EINVAL; } } @@ -4790,15 +6204,40 @@ static int gsm48_rr_rx_acch(struct osmocom_ms *ms, struct msgb *msg) /* unit data from layer 2 to RR layer */ static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg) { + struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm_settings *set = &ms->settings; struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); struct tlv_parsed tv; + int ind_ta = -1, ind_tx_power = -1; uint8_t ch_type, ch_subch, ch_ts; DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n", rllh->chan_nr, rllh->link_id); - rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); + if (rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg) - sizeof(*rllh)) < 0) { + LOGP(DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return -EINVAL; + } + + /* Update TX power and timing advance, if included in message. */ + if (TLVP_PRES_LEN(&tv, RSL_IE_TIMING_ADVANCE, 1)) { + ind_ta = *TLVP_VAL(&tv, RSL_IE_TIMING_ADVANCE); + LOGP(DRR, LOGL_INFO, "DL SACCH indicates ta %d (actual ta %d)\n", ind_ta, ind_ta - set->alter_delay); + } + if (TLVP_PRES_LEN(&tv, RSL_IE_MS_POWER, 1)) { + ind_tx_power = *TLVP_VAL(&tv, RSL_IE_MS_POWER); + LOGP(DRR, LOGL_INFO, "DL SACCH indicates tx_power %d\n", ind_tx_power); + } + if ((ind_ta >= 0 && ind_ta != rr->cd_now.ind_ta) || + (ind_tx_power >= 0 && ind_tx_power != rr->cd_now.ind_tx_power)) { + LOGP(DRR, LOGL_INFO, "Applying new ta and tx_power\n"); + l1ctl_tx_param_req(ms, ind_ta - set->alter_delay, + (set->alter_tx_power) ? set->alter_tx_power_value : ind_tx_power); + rr->cd_now.ind_ta = ind_ta; + rr->cd_now.ind_tx_power = ind_tx_power; + } + if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n"); return -EIO; @@ -4814,6 +6253,10 @@ static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg) LOGP(DCS, LOGL_INFO, "Channel provides data.\n"); cs->ccch_state = GSM322_CCCH_ST_DATA; + /* in group receive mode */ + if (ms->rrlayer.vgcs.group_state == GSM48_RR_GST_RECEIVE) + return gsm48_rr_group_req_continue(ms); + /* in dedicated mode */ if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND) return gsm48_rr_tx_rand_acc(ms, NULL); @@ -4832,7 +6275,13 @@ static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg) // TODO: timer depends on BCCH config } - rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRSL, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, rllh->chan_nr); + return -EINVAL; + } + switch (ch_type) { case RSL_CHAN_PCH_AGCH: return gsm48_rr_rx_pch_agch(ms, msg); @@ -4842,6 +6291,7 @@ static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg) case RSL_CHAN_Lm_ACCHs: case RSL_CHAN_SDCCH4_ACCH: case RSL_CHAN_SDCCH8_ACCH: + msg->cb[0] = rllh->link_id; return gsm48_rr_rx_acch(ms, msg); default: LOGP(DRSL, LOGL_NOTICE, "RSL with chan_nr 0x%02x unknown.\n", @@ -4900,6 +6350,12 @@ static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg) uint16_t ma[64]; uint8_t ma_len; + /* Handle on group channel. */ + if (rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) { + LOGP(DRR, LOGL_INFO, "Returning to group receive mode, due to link release confirm.\n"); + return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_NORMAL); + } + /* switch back to old channel, if modify/ho failed */ switch (rr->modify_state) { case GSM48_RR_MOD_ASSIGN: @@ -4946,7 +6402,11 @@ static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg) stop_rr_t3110(rr); /* send release indication */ - nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); + if (rr->vgcs.group_state == GSM48_RR_GST_RECEIVE) { + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_GROUP_REL_IND); + rr->vgcs.group_state = GSM48_RR_GST_OFF; + } else + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); if (!nmsg) return -ENOMEM; nrrh = (struct gsm48_rr_hdr *)nmsg->data; @@ -4994,6 +6454,12 @@ static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg) if (rr->modify_state) return 0; + /* Handle on group channel. */ + if ((link_id & 7) == 0 && rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) { + LOGP(DRR, LOGL_INFO, "Returning to group receive mode, due to link failure.\n"); + return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_LINK_FAILURE); + } + /* send abort ind to upper layer */ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_ABORT_IND); if (!nmsg) @@ -5156,7 +6622,13 @@ static int gsm48_rr_est_req_sapi3(struct osmocom_ms *ms, struct msgb *msg) return gsm48_rr_upmsg(ms, nmsg); } - rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRSL, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, rr->cd_now.chan_nr); + return -EINVAL; + } + if (ch_type != RSL_CHAN_Bm_ACCHs && ch_type != RSL_CHAN_Lm_ACCHs) { LOGP(DRR, LOGL_INFO, "Requesting DCCH link, because no TCH " @@ -5250,11 +6722,6 @@ static struct dldatastate { {SBIT(GSM48_RR_ST_DEDICATED), RSL_MT_SUSP_CONF, gsm48_rr_susp_cnf_dedicated}, -#if 0 - {SBIT(GSM48_RR_ST_DEDICATED), - RSL_MT_CHAN_CNF, gsm48_rr_rand_acc_cnf_dedicated}, -#endif - {SBIT(GSM48_RR_ST_DEDICATED), RSL_MT_ERROR_IND, gsm48_rr_mdl_error_ind}, }; @@ -5351,11 +6818,24 @@ static int gsm48_rcv_cch(struct osmocom_ms *ms, struct msgb *msg) "%s\n", ms->name, rsl_msg_name(msg_type), gsm48_rr_state_names[rr->state]); - if (rr->state == GSM48_RR_ST_CONN_PEND - && msg_type == RSL_MT_CHAN_CONF) { - rc = gsm48_rr_tx_rand_acc(ms, msg); - msgb_free(msg); - return rc; + if (msg_type == RSL_MT_CHAN_CONF) { + /* Recevie confirm to get the FN when the access burst was transmitted on VGCS channel. */ + if (rr->state == GSM48_RR_ST_CONN_PEND && rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) { + rc = gsm48_rr_uplink_access(ms, msg); + msgb_free(msg); + return rc; + } + /* Ignore subsequent access bursts in dedicated group transmit mode. */ + if (rr->state == GSM48_RR_ST_DEDICATED && rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) { + msgb_free(msg); + return 0; + } + /* Recevie confirm to get the FN when the access burst was transmitted on CCCH. */ + if (rr->state == GSM48_RR_ST_CONN_PEND) { + rc = gsm48_rr_tx_rand_acc(ms, msg); + msgb_free(msg); + return rc; + } } LOGP(DRSL, LOGL_NOTICE, "RSLms message unhandled\n"); @@ -5364,7 +6844,7 @@ static int gsm48_rcv_cch(struct osmocom_ms *ms, struct msgb *msg) } -/* input function for L2 messags up to L3 */ +/* input function for L2 messages up to L3 */ static int gsm48_rcv_rsl(struct osmocom_ms *ms, struct msgb *msg) { struct abis_rsl_common_hdr *rslh = msgb_l2(msg); @@ -5407,6 +6887,20 @@ static struct rrdownstate { {SBIT(GSM48_RR_ST_CONN_PEND) | SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.13.3 */ GSM48_RR_ABORT_REQ, gsm48_rr_abort_req}, + + /* NOTE: If not IDLE, it is rejected there. */ + {ALL_STATES, /* 3.3.3.2 */ + GSM48_RR_GROUP_REQ, gsm48_rr_group_req}, + + {ALL_STATES, + GSM48_RR_GROUP_REL_REQ, gsm48_rr_group_rel_req}, + + /* NOTE: If not IDLE, it is rejected there. */ + {ALL_STATES, /* 3.3.1.2 */ + GSM48_RR_UPLINK_REQ, gsm48_rr_uplink_req}, + + {ALL_STATES, /* 3.4.13.4 */ + GSM48_RR_UPLINK_REL_REQ, gsm48_rr_uplink_rel_req}, }; #define RRDOWNSLLEN \ @@ -5501,7 +6995,11 @@ int gsm48_rr_init(struct osmocom_ms *ms) start_rr_t_meas(rr, 1, 0); - rr->audio_mode = AUDIO_TX_MICROPHONE | AUDIO_RX_SPEAKER; + rr->tch_loop_mode = L1CTL_TCH_LOOP_OPEN; + rr->audio_mode = 0x00; /* set in tch_{voice,data}_state_init() */ + + /* List of notifications about ongoing ASCI calls */ + INIT_LLIST_HEAD(&rr->vgcs.notif_list); return 0; } @@ -5531,6 +7029,12 @@ int gsm48_rr_exit(struct osmocom_ms *ms) stop_rr_t3122(rr); stop_rr_t3124(rr); stop_rr_t3126(rr); + stop_rr_t_ul_free(rr); + stop_rr_t3128(rr); + stop_rr_t3130(rr); + + /* Free pending list entries. */ + asci_notif_list_free(rr); return 0; } @@ -5574,75 +7078,34 @@ static void start_rr_t3124(struct gsm48_rrlayer *rr, int sec, int micro) osmo_timer_schedule(&rr->t3124, sec, micro); } -/* send HANDOVER ACCESS burst (9.1.14) */ -static int gsm48_rr_tx_hando_access(struct osmocom_ms *ms) -{ - nmsg = msgb_alloc_headroom(20, 16, "HAND_ACCESS"); - if (!nmsg) - return -ENOMEM; - *msgb_put(nmsg, 1) = rr->hando_ref; - todo burst - return gsm48_send_rsl(ms, RSL_MT_RAND_ACC_REQ, nmsg, 0); -} - -/* send next channel request in dedicated state */ -static int gsm48_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg) -{ - struct gsm48_rrlayer *rr = &ms->rrlayer; - struct msgb *nmsg; - int s; - - if (rr->modify_state != GSM48_RR_MOD_HANDO) { - LOGP(DRR, LOGL_NOTICE, "Random acces confirm, but not in handover state.\n"); - return 0; - } - - /* send up to four handover access bursts */ - if (rr->hando_acc_left) { - rr->hando_acc_left--; - gsm48_rr_tx_hando_access(ms); - return; - } - - /* start timer for sending next HANDOVER ACCESS bursts afterwards */ - if (!osmo_timer_pending(&rr->t3124)) { - if (allocated channel is SDCCH) - start_rr_t3124(rr, GSM_T3124_675); - else - start_rr_t3124(rr, GSM_T3124_320); - } - if (!rr->n_chan_req) { - start_rr_t3126(rr, 5, 0); /* TODO improve! */ - return 0; - } - rr->n_chan_req--; - - /* wait for PHYSICAL INFORMATION message or T3124 timeout */ - return 0; - -} - #endif -int gsm48_rr_tx_voice(struct osmocom_ms *ms, struct msgb *msg) +int gsm48_rr_tx_traffic(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; uint8_t ch_type, ch_subch, ch_ts; if (!rr->dm_est) { LOGP(DRR, LOGL_INFO, "Current channel is not active\n"); - msgb_free(msg); - return -ENOTSUP; + goto error; } - rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts); - if (ch_type != RSL_CHAN_Bm_ACCHs) { - LOGP(DRR, LOGL_INFO, "Current channel is not (yet) TCH/F\n"); - msgb_free(msg); - return -ENOTSUP; + if (rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRSL, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, rr->cd_now.chan_nr); + goto error; + } + + if (ch_type != RSL_CHAN_Bm_ACCHs && ch_type != RSL_CHAN_Lm_ACCHs) { + LOGP(DRR, LOGL_INFO, "Current channel is not (yet) TCH\n"); + goto error; } return l1ctl_tx_traffic_req(ms, msg, rr->cd_now.chan_nr, 0); +error: + msgb_free(msg); + return -ENOTSUP; } int gsm48_rr_audio_mode(struct osmocom_ms *ms, uint8_t mode) @@ -5657,11 +7120,16 @@ int gsm48_rr_audio_mode(struct osmocom_ms *ms, uint8_t mode) if (!rr->dm_est) return 0; - rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRSL, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, rr->cd_now.chan_nr); + return -EINVAL; + } + if (ch_type != RSL_CHAN_Bm_ACCHs && ch_type != RSL_CHAN_Lm_ACCHs) return 0; - return l1ctl_tx_tch_mode_req(ms, rr->cd_now.mode, mode); + return l1ctl_tx_tch_mode_req(ms, rr->cd_now.mode, mode, rr->cd_now.tch_flags, rr->tch_loop_mode); } - |