/* * (C) 2010 by Andreas Eversberg * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ /* Very short description of some of the procedures: * * A radio ressource request causes sendig a channel request on RACH. * After receiving of an immediate assignment the link will be establised. * After the link is established, the dedicated mode is entered and confirmed. * * A Paging request also triggers the channel request as above... * After the link is established, the dedicated mode is entered and indicated. * * During dedicated mode, messages are transferred. * * When an assignment command or a handover command is received, the current * link is released. After release, the new channel is activated and the * link is established again. After link is establised, pending messages from * radio ressource are sent. * * 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) 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. */ //#define TEST_STARTING_TIMER 140 /* Testing if frequency modification works correctly "after time". * * When enabled, the starting time will be set in the future. * A wrong channel is defined "before time", so noise is received until * starting time elapses. * If a starting time is given by the network, this time is ignored. * Also channel definitions "before time" are ignored. * * NOTE: TEST_STARTING_TIMER MUST be defined also. */ //#define TEST_FREQUENCY_MOD #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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, 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 */ /* 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) { *power_level = pc->power_level; if (atc) /* only in case of 10.5.2.28a */ *atc = pc->atc; return 0; } /* 10.5.2.38 decode Starting time IE */ static int gsm48_decode_start_time(struct gsm48_rr_cd *cd, struct gsm48_start_time *st) { cd->start = 1; cd->start_tm.t1 = st->t1; cd->start_tm.t2 = st->t2; cd->start_tm.t3 = (st->t3_high << 3) | st->t3_low; cd->start_tm.fn = gsm_gsmtime2fn(&cd->start_tm); return 0; } /* decode "BA Range" (10.5.2.1a) */ static int gsm48_decode_ba_range(const uint8_t *ba, uint8_t ba_len, uint32_t *range, uint8_t *ranges, int max_ranges) { /* ba = pointer to IE without IE type and length octets * ba_len = number of octets * range = pointer to store decoded range * ranges = number of ranges decoded * max_ranges = maximum number of decoded ranges that can be stored */ uint16_t lower, higher; int i, n, required_octets; /* find out how much ba ranges will be decoded */ n = *ba++; ba_len --; required_octets = 5 * (n >> 1) + 3 * (n & 1); if (required_octets > ba_len) { LOGP(DRR, LOGL_NOTICE, "BA range IE too short: %d ranges " "require %d octets, but only %d octets remain.\n", n, required_octets, ba_len); *ranges = 0; return -EINVAL; } if (n > max_ranges) { LOGP(DRR, LOGL_NOTICE, "BA range %d exceed the maximum number " "of ranges supported by this mobile (%d).\n", n, max_ranges); n = max_ranges; } /* decode ranges */ for (i = 0; i < n; i++) { if (!(i & 1)) { /* decode even range number */ lower = *ba++ << 2; lower |= (*ba >> 6); higher = (*ba++ & 0x3f) << 4; higher |= *ba >> 4; } else { lower = (*ba++ & 0x0f) << 6; lower |= *ba >> 2; higher = (*ba++ & 0x03) << 8; higher |= *ba++; /* decode odd range number */ } *range++ = (higher << 16) | lower; } *ranges = n; return 0; } /* decode "Cell Description" (10.5.2.2) */ static int gsm48_decode_cell_desc(struct gsm48_cell_desc *cd, uint16_t *arfcn, uint8_t *ncc, uint8_t *bcc) { *arfcn = (cd->arfcn_hi << 8) + cd->arfcn_lo; *ncc = cd->ncc; *bcc = cd->bcc; return 0; } /* decode "Synchronization Indication" (10.5.2.39) */ static int gsm48_decode_sync_ind(struct gsm48_rrlayer *rr, struct gsm48_sync_ind *si) { rr->hando_sync_ind = si->si; rr->hando_rot = si->rot; rr->hando_nci = si->nci; return 0; } /* 3.1.4.3 set sequence number and increment */ static int gsm48_apply_v_sd(struct gsm48_rrlayer *rr, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); uint8_t pdisc = gh->proto_discr & 0x0f; uint8_t v_sd; switch (pdisc) { case GSM48_PDISC_MM: case GSM48_PDISC_CC: case GSM48_PDISC_NC_SS: /* all three pdiscs share the same V(SD) */ pdisc = GSM48_PDISC_MM; // fall through case GSM48_PDISC_GROUP_CC: case GSM48_PDISC_BCAST_CC: case GSM48_PDISC_PDSS1: case GSM48_PDISC_PDSS2: /* extract v_sd(pdisc) */ v_sd = (rr->v_sd >> pdisc) & 1; /* replace bit 7 vy v_sd */ gh->msg_type &= 0xbf; gh->msg_type |= (v_sd << 6); /* increment V(SD) */ rr->v_sd ^= (1 << pdisc); LOGP(DRR, LOGL_INFO, "Using and incrementing V(SD) = %d " "(pdisc %x)\n", v_sd, pdisc); break; case GSM48_PDISC_RR: case GSM48_PDISC_SMS: /* no V(VSD) is required */ break; default: LOGP(DRR, LOGL_ERROR, "Error, V(SD) of pdisc %x not handled\n", pdisc); return -ENOTSUP; } return 0; } /* set channel mode if supported, or return error cause */ static uint8_t gsm48_rr_check_mode(struct osmocom_ms *ms, uint8_t chan_nr, uint8_t mode) { 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 */ if (ch_type != RSL_CHAN_Bm_ACCHs && ch_type != RSL_CHAN_Lm_ACCHs) return 0; switch (mode) { case GSM48_CMODE_SIGN: LOGP(DRR, LOGL_INFO, "Mode: signalling\n"); break; case GSM48_CMODE_SPEECH_V1: if (ch_type == RSL_CHAN_Bm_ACCHs) { if (!set->full_v1) { LOGP(DRR, LOGL_NOTICE, "Not supporting " "full-rate speech V1\n"); return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; } LOGP(DRR, LOGL_INFO, "Mode: full-rate speech V1\n"); } else { if (!set->half_v1) { LOGP(DRR, LOGL_NOTICE, "Not supporting " "half-rate speech V1\n"); return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; } LOGP(DRR, LOGL_INFO, "Mode: half-rate speech V1\n"); } break; case GSM48_CMODE_SPEECH_EFR: if (ch_type == RSL_CHAN_Bm_ACCHs) { if (!set->full_v2) { LOGP(DRR, LOGL_NOTICE, "Not supporting " "full-rate speech V2\n"); return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; } LOGP(DRR, LOGL_INFO, "Mode: full-rate speech V2\n"); } else { LOGP(DRR, LOGL_NOTICE, "Not supporting " "half-rate speech V2\n"); return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; } break; case GSM48_CMODE_SPEECH_AMR: if (ch_type == RSL_CHAN_Bm_ACCHs) { if (!set->full_v3) { LOGP(DRR, LOGL_NOTICE, "Not supporting " "full-rate speech V3\n"); return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; } LOGP(DRR, LOGL_INFO, "Mode: full-rate speech V3\n"); } else { if (!set->half_v3) { LOGP(DRR, LOGL_NOTICE, "Not supporting " "half-rate speech V3\n"); return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; } 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; } return 0; } /* apply new "alter_delay" in dedicated mode */ int gsm48_rr_alter_delay(struct osmocom_ms *ms) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm_settings *set = &ms->settings; if (rr->state != GSM48_RR_ST_DEDICATED) return -EINVAL; l1ctl_tx_param_req(ms, rr->cd_now.ind_ta - set->alter_delay, (set->alter_tx_power) ? set->alter_tx_power_value : rr->cd_now.ind_tx_power); return 0; } /* * state transition */ const char *gsm48_rr_state_names[] = { "idle", "connection pending", "dedicated", "release pending", }; 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; /* 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]); /* abort handover, in case of release of dedicated mode */ if (rr->state == GSM48_RR_ST_DEDICATED) { /* disable handover / assign state */ rr->modify_state = GSM48_RR_MOD_NONE; /* stop start_time_timer */ stop_rr_t_starting(rr); /* stop handover timer */ stop_rr_t3124(rr); } rr->state = state; 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; rr->dm_est = 0; l1ctl_tx_reset_req(rr->ms, L1CTL_RES_T_FULL); /* 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); /* clear all descriptions of last channel */ memset(&rr->cd_now, 0, sizeof(rr->cd_now)); /* reset ciphering */ rr->cipher_on = 0; /* reset audio mode */ /* tell cell selection process to return to idle mode * NOTE: this must be sent unbuffered, because it will * leave camping state, so it locks against subsequent * establishment of dedicated channel, before the * cell selection process returned to camping state * again. (after cell reselection) */ nmsg = gsm322_msgb_alloc(GSM322_EVENT_RET_IDLE); if (!nmsg) return; /* return to same cell after LOC.UPD. */ if (rr->est_cause == RR_EST_CAUSE_LOC_UPD) { em = (struct gsm322_msg *) nmsg->data; em->same_cell = 1; } gsm322_c_event(rr->ms, nmsg); 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; } } const char *gsm48_sapi3_state_names[] = { "idle", "wait establishment", "established", "wait release", }; static void new_sapi3_state(struct gsm48_rrlayer *rr, int state) { if (state < 0 || state >= (sizeof(gsm48_sapi3_state_names) / sizeof(char *))) return; LOGP(DRR, LOGL_INFO, "new SAPI 3 link state %s -> %s\n", gsm48_sapi3_state_names[rr->sapi3_state], gsm48_sapi3_state_names[state]); rr->sapi3_state = state; } /* * messages */ /* names of RR-SAP */ static const struct value_string gsm48_rr_msg_names[] = { { GSM48_RR_EST_REQ, "RR_EST_REQ" }, { GSM48_RR_EST_IND, "RR_EST_IND" }, { GSM48_RR_EST_CNF, "RR_EST_CNF" }, { GSM48_RR_REL_IND, "RR_REL_IND" }, { GSM48_RR_SYNC_IND, "RR_SYNC_IND" }, { GSM48_RR_DATA_REQ, "RR_DATA_REQ" }, { GSM48_RR_DATA_IND, "RR_DATA_IND" }, { GSM48_RR_UNIT_DATA_IND, "RR_UNIT_DATA_IND" }, { 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 } }; const char *get_rr_name(int value) { return get_value_string(gsm48_rr_msg_names, value); } /* allocate GSM 04.08 layer 3 message */ struct msgb *gsm48_l3_msgb_alloc(void) { struct msgb *msg; msg = msgb_alloc_headroom(L3_ALLOC_SIZE+L3_ALLOC_HEADROOM, L3_ALLOC_HEADROOM, "GSM 04.08 L3"); if (!msg) return NULL; msg->l3h = msg->data; return msg; } /* allocate GSM 04.06 layer 2 RSL message */ struct msgb *gsm48_rsl_msgb_alloc(void) { struct msgb *msg; msg = msgb_alloc_headroom(RSL_ALLOC_SIZE+RSL_ALLOC_HEADROOM, RSL_ALLOC_HEADROOM, "GSM 04.06 RSL"); if (!msg) return NULL; msg->l2h = msg->data; return msg; } /* allocate GSM 04.08 message (RR-SAP) */ struct msgb *gsm48_rr_msgb_alloc(int msg_type) { struct msgb *msg; struct gsm48_rr_hdr *rrh; msg = msgb_alloc_headroom(RR_ALLOC_SIZE+RR_ALLOC_HEADROOM, RR_ALLOC_HEADROOM, "GSM 04.08 RR"); if (!msg) return NULL; rrh = (struct gsm48_rr_hdr *) msgb_put(msg, sizeof(*rrh)); rrh->msg_type = msg_type; return msg; } /* queue message (RR-SAP) */ int gsm48_rr_upmsg(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_mmlayer *mm = &ms->mmlayer; msgb_enqueue(&mm->rr_upqueue, msg); return 0; } /* push rsl header and send (RSL-SAP) */ 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; if (!msg->l3h) { LOGP(DRR, LOGL_ERROR, "FIX l3h\n"); return -EINVAL; } rsl_rll_push_l3(msg, msg_type, rr->cd_now.chan_nr, link_id, 1); return lapdm_rslms_recvmsg(msg, &ms->lapdm_channel); } /* push rsl header without L3 info and send (RSL-SAP) */ static int gsm48_send_rsl_nol3(struct osmocom_ms *ms, uint8_t msg_type, struct msgb *msg, uint8_t link_id) { struct gsm48_rrlayer *rr = &ms->rrlayer; rsl_rll_push_hdr(msg, msg_type, rr->cd_now.chan_nr, link_id, 1); return lapdm_rslms_recvmsg(msg, &ms->lapdm_channel); } /* enqueue messages (RSL-SAP) */ static int rcv_rsl(struct msgb *msg, struct lapdm_entity *le, void *l3ctx) { struct osmocom_ms *ms = l3ctx; struct gsm48_rrlayer *rr = &ms->rrlayer; msgb_enqueue(&rr->rsl_upqueue, msg); return 0; } /* dequeue messages (RSL-SAP) */ int gsm48_rsl_dequeue(struct osmocom_ms *ms) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct msgb *msg; int work = 0; while ((msg = msgb_dequeue(&rr->rsl_upqueue))) { /* msg is freed there */ gsm48_rcv_rsl(ms, msg); work = 1; /* work done */ } return work; } int gsm48_rr_start_monitor(struct osmocom_ms *ms) { ms->rrlayer.monitor = 1; return 0; } int gsm48_rr_stop_monitor(struct osmocom_ms *ms) { ms->rrlayer.monitor = 0; return 0; } /* release L3 link in both directions in case of main link release */ static int gsm48_release_sapi3_link(struct osmocom_ms *ms) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm48_rr_hdr *nrrh; struct msgb *nmsg; uint8_t *mode; if (rr->sapi3_state == GSM48_RR_SAPI3ST_IDLE) return 0; LOGP(DRR, LOGL_INFO, "Main signallin link is down, so release SAPI 3 " "link locally.\n"); new_sapi3_state(rr, GSM48_RR_SAPI3ST_IDLE); /* disconnect the SAPI 3 signalling link */ nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; mode = msgb_put(nmsg, 2); mode[0] = RSL_IE_RELEASE_MODE; mode[1] = 1; /* local release */ gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, rr->sapi3_link_id); /* send inication to upper layer */ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); if (!nmsg) return -ENOMEM; nrrh = (struct gsm48_rr_hdr *)nmsg->data; nrrh->cause = RR_REL_CAUSE_NORMAL; nrrh->sapi = rr->sapi3_link_id & 7; gsm48_rr_upmsg(ms, nmsg); return 0; } /* * timers handling */ /* special timer to monitor measurements */ static void timeout_rr_meas(void *arg) { struct gsm48_rrlayer *rr = arg; struct gsm322_cellsel *cs = &rr->ms->cellsel; struct rx_meas_stat *meas = &rr->ms->meas; 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]; 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) { OSMO_STRBUF_PRINTF(sb, "MON: not camping on serving cell"); goto restart; } else if (!meas->frames) { 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; 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) { 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) l23_vty_ms_notify(rr->ms, "%s\n", text); if (rr->dm_est) gsm48_rr_tx_meas_rep(rr->ms); restart: meas->frames = meas->snr = meas->berr = meas->rxlev = 0; start_rr_t_meas(rr, 1, 0); } /* special timer to assign / handover when starting time is reached */ static void timeout_rr_t_starting(void *arg) { struct gsm48_rrlayer *rr = arg; struct msgb *nmsg; LOGP(DRR, LOGL_INFO, "starting timer has fired\n"); /* open channel when starting timer of IMM.ASS has fired */ if (rr->modify_state == GSM48_RR_MOD_IMM_ASS) { rr->modify_state = GSM48_RR_MOD_NONE; gsm48_rr_dl_est(rr->ms); return; } /* start suspension of current link */ LOGP(DRR, LOGL_INFO, "request suspension of data link\n"); nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return; gsm48_send_rsl(rr->ms, RSL_MT_SUSP_REQ, nmsg, 0); /* release SAPI 3 link, if exits * FIXME: suspend and resume afterward */ gsm48_release_sapi3_link(rr->ms); } /* special timer to ensure that UA is sent before disconnecting channel */ static void timeout_rr_t_rel_wait(void *arg) { struct gsm48_rrlayer *rr = arg; LOGP(DRR, LOGL_INFO, "L2 release timer has fired, done waiting\n"); /* return to idle now */ new_rr_state(rr, GSM48_RR_ST_IDLE); } /* 3.4.13.1.1: Timeout of T3110 */ static void timeout_rr_t3110(void *arg) { struct gsm48_rrlayer *rr = arg; struct osmocom_ms *ms = rr->ms; struct msgb *nmsg; uint8_t *mode; LOGP(DRR, LOGL_INFO, "timer T3110 has fired, release locally\n"); new_rr_state(rr, GSM48_RR_ST_REL_PEND); /* disconnect the main signalling link */ nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return; mode = msgb_put(nmsg, 2); mode[0] = RSL_IE_RELEASE_MODE; mode[1] = 1; /* local release */ gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0); /* release SAPI 3 link, if exits */ gsm48_release_sapi3_link(ms); return; } static void timeout_rr_t3122(void *arg) { LOGP(DRR, LOGL_INFO, "timer T3122 has fired\n"); } static void timeout_rr_t3126(void *arg) { struct gsm48_rrlayer *rr = arg; struct osmocom_ms *ms = rr->ms; LOGP(DRR, LOGL_INFO, "timer T3126 has fired\n"); if (rr->rr_est_req) { struct msgb *msg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); struct gsm48_rr_hdr *rrh; LOGP(DSUM, LOGL_INFO, "Requesting channel failed\n"); if (!msg) return; rrh = (struct gsm48_rr_hdr *)msg->data; rrh->cause = RR_REL_CAUSE_RA_FAILURE; gsm48_rr_upmsg(ms, msg); } 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; rr->t_meas.data = rr; osmo_timer_schedule(&rr->t_meas, sec, micro); } static void start_rr_t_rel_wait(struct gsm48_rrlayer *rr, int sec, int micro) { LOGP(DRR, LOGL_INFO, "starting T_rel_wait with %d.%03d seconds\n", sec, micro / 1000); rr->t_rel_wait.cb = timeout_rr_t_rel_wait; rr->t_rel_wait.data = rr; osmo_timer_schedule(&rr->t_rel_wait, sec, micro); } static void start_rr_t_starting(struct gsm48_rrlayer *rr, int sec, int micro) { LOGP(DRR, LOGL_INFO, "starting T_starting with %d.%03d seconds\n", sec, micro / 1000); rr->t_starting.cb = timeout_rr_t_starting; rr->t_starting.data = rr; osmo_timer_schedule(&rr->t_starting, sec, micro); } static void start_rr_t3110(struct gsm48_rrlayer *rr, int sec, int micro) { LOGP(DRR, LOGL_INFO, "starting T3110 with %d.%03d seconds\n", sec, micro / 1000); rr->t3110.cb = timeout_rr_t3110; rr->t3110.data = rr; osmo_timer_schedule(&rr->t3110, sec, micro); } static void start_rr_t3122(struct gsm48_rrlayer *rr, int sec, int micro) { LOGP(DRR, LOGL_INFO, "starting T3122 with %d.%03d seconds\n", sec, micro / 1000); rr->t3122.cb = timeout_rr_t3122; rr->t3122.data = rr; osmo_timer_schedule(&rr->t3122, sec, micro); } static void start_rr_t3126(struct gsm48_rrlayer *rr, int sec, int micro) { LOGP(DRR, LOGL_INFO, "starting T3126 with %d.%03d seconds\n", sec, micro / 1000); rr->t3126.cb = timeout_rr_t3126; rr->t3126.data = rr; 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)) { LOGP(DRR, LOGL_INFO, "stopping pending timer T_meas\n"); osmo_timer_del(&rr->t_meas); } } static void stop_rr_t_starting(struct gsm48_rrlayer *rr) { if (osmo_timer_pending(&rr->t_starting)) { LOGP(DRR, LOGL_INFO, "stopping pending timer T_starting\n"); osmo_timer_del(&rr->t_starting); } } static void stop_rr_t_rel_wait(struct gsm48_rrlayer *rr) { if (osmo_timer_pending(&rr->t_rel_wait)) { LOGP(DRR, LOGL_INFO, "stopping pending timer T_rel_wait\n"); osmo_timer_del(&rr->t_rel_wait); } } static void stop_rr_t3110(struct gsm48_rrlayer *rr) { if (osmo_timer_pending(&rr->t3110)) { LOGP(DRR, LOGL_INFO, "stopping pending timer T3110\n"); osmo_timer_del(&rr->t3110); } } static void stop_rr_t3122(struct gsm48_rrlayer *rr) { if (osmo_timer_pending(&rr->t3122)) { LOGP(DRR, LOGL_INFO, "stopping pending timer T3122\n"); osmo_timer_del(&rr->t3122); } } static void stop_rr_t3124(struct gsm48_rrlayer *rr) { if (osmo_timer_pending(&rr->t3124)) { LOGP(DRR, LOGL_INFO, "stopping pending timer T3124\n"); osmo_timer_del(&rr->t3124); } } static void stop_rr_t3126(struct gsm48_rrlayer *rr) { if (osmo_timer_pending(&rr->t3126)) { LOGP(DRR, LOGL_INFO, "stopping pending timer T3126\n"); osmo_timer_del(&rr->t3126); } } 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 */ int gsm48_rr_tx_rr_status(struct osmocom_ms *ms, uint8_t cause) { struct msgb *nmsg; struct gsm48_hdr *gh; struct gsm48_rr_status *st; LOGP(DRR, LOGL_INFO, "RR STATUS (cause #%d)\n", cause); nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); st = (struct gsm48_rr_status *) msgb_put(nmsg, sizeof(*st)); gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_STATUS; /* rr cause */ st->rr_cause = cause; return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg, 0); } /* * ciphering */ /* send chiperhing mode complete */ static int gsm48_rr_tx_cip_mode_cpl(struct osmocom_ms *ms, uint8_t cr) { struct msgb *nmsg; struct gsm48_hdr *gh; struct gsm48_rr_hdr *nrrh; LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMPLETE (cr %d)\n", cr); nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_CIPH_M_COMPL; /* MI */ if (cr) gsm48_encode_mi_tlv(ms, nmsg, GSM_MI_TYPE_IMEISV, false); gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg, 0); /* send RR_SYNC_IND(ciphering) */ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_SYNC_IND); if (!nmsg) return -ENOMEM; nrrh = (struct gsm48_rr_hdr *)nmsg->data; nrrh->cause = RR_SYNC_CAUSE_CIPHERING; return gsm48_rr_upmsg(ms, nmsg); } /* receive ciphering mode command */ static int gsm48_rr_rx_cip_mode_cmd(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm_subscriber *subscr = &ms->subscr; struct gsm_settings *set = &ms->settings; struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_cip_mode_cmd *cm = (struct gsm48_cip_mode_cmd *)gh->data; int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cm); uint8_t sc, alg_id, cr; if (payload_len < 0) { LOGP(DRR, LOGL_NOTICE, "Short read of CIPHERING MODE COMMAND " "message.\n"); return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); } /* cipher mode setting */ sc = cm->sc; alg_id = cm->alg_id; /* cipher mode response */ cr = cm->cr; if (!sc) LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMMAND (sc=%u, cr=%u)\n", sc, cr); else LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMMAND (sc=%u, " "algo=A5/%d cr=%u)\n", sc, alg_id + 1, cr); /* 3.4.7.2 */ if (rr->cipher_on && sc) { LOGP(DRR, LOGL_NOTICE, "chiphering already applied\n"); return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); } /* check if we actually support this cipher */ if (sc && ((alg_id == GSM_CIPHER_A5_1 && !set->a5_1) || (alg_id == GSM_CIPHER_A5_2 && !set->a5_2) || (alg_id == GSM_CIPHER_A5_3 && !set->a5_3) || (alg_id == GSM_CIPHER_A5_4 && !set->a5_4) || (alg_id == GSM_CIPHER_A5_5 && !set->a5_5) || (alg_id == GSM_CIPHER_A5_6 && !set->a5_6) || (alg_id == GSM_CIPHER_A5_7 && !set->a5_7))) { LOGP(DRR, LOGL_NOTICE, "algo not supported\n"); return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); } /* check if we have no key */ if (sc && subscr->key_seq == 7) { LOGP(DRR, LOGL_NOTICE, "no key available\n"); return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); } /* change to ciphering */ rr->cipher_on = sc; rr->cipher_type = alg_id; if (rr->cipher_on) l1ctl_tx_crypto_req(ms, rr->cd_now.chan_nr, rr->cipher_type + 1, subscr->key, 8); else l1ctl_tx_crypto_req(ms, rr->cd_now.chan_nr, 0, NULL, 0); /* response (using the new mode) */ return gsm48_rr_tx_cip_mode_cpl(ms, cr); } /* * classmark */ /* Encode "Classmark 3" (10.5.1.7) */ static int gsm48_rr_enc_cm3(struct osmocom_ms *ms, uint8_t *buf, uint8_t *len) { struct gsm_support *sup = &ms->support; struct gsm_settings *set = &ms->settings; struct bitvec bv; memset(&bv, 0, sizeof(bv)); bv.data = buf; bv.data_len = 12; /* spare bit */ bitvec_set_bit(&bv, 0); /* band 3 supported */ if (set->dcs) bitvec_set_bit(&bv, ONE); else bitvec_set_bit(&bv, ZERO); /* band 2 supported */ if (set->e_gsm || set->r_gsm) bitvec_set_bit(&bv, ONE); else bitvec_set_bit(&bv, ZERO); /* band 1 supported */ if (set->p_gsm && !(set->e_gsm || set->r_gsm)) bitvec_set_bit(&bv, ONE); else bitvec_set_bit(&bv, ZERO); /* a5 bits */ if (set->a5_7) bitvec_set_bit(&bv, ONE); else bitvec_set_bit(&bv, ZERO); if (set->a5_6) bitvec_set_bit(&bv, ONE); else bitvec_set_bit(&bv, ZERO); if (set->a5_5) bitvec_set_bit(&bv, ONE); else bitvec_set_bit(&bv, ZERO); if (set->a5_4) bitvec_set_bit(&bv, ONE); else bitvec_set_bit(&bv, ZERO); /* radio capability */ if (!set->dcs && !set->p_gsm && !(set->e_gsm || set->r_gsm)) { /* Fig. 10.5.7 / TS 24.0008: none of dcs, p, e, r */ } else if (set->dcs && !set->p_gsm && !(set->e_gsm || set->r_gsm)) { /* dcs only */ bitvec_set_uint(&bv, 0, 4); bitvec_set_uint(&bv, set->class_dcs, 4); } else if (set->dcs && (set->p_gsm || (set->e_gsm || set->r_gsm))) { /* dcs */ bitvec_set_uint(&bv, set->class_dcs, 4); /* low band */ bitvec_set_uint(&bv, set->class_900, 4); } else { /* low band only */ bitvec_set_uint(&bv, 0, 4); bitvec_set_uint(&bv, set->class_900, 4); } /* r support */ if (set->r_gsm) { bitvec_set_bit(&bv, ONE); bitvec_set_uint(&bv, set->class_900, 3); } else { bitvec_set_bit(&bv, ZERO); } /* multi slot support */ if (sup->ms_sup) { bitvec_set_bit(&bv, ONE); bitvec_set_uint(&bv, sup->ms_sup, 5); } else { bitvec_set_bit(&bv, ZERO); } /* ucs2 treatment */ if (sup->ucs2_treat) { bitvec_set_bit(&bv, ONE); } else { bitvec_set_bit(&bv, ZERO); } /* support extended measurements */ if (sup->ext_meas) { bitvec_set_bit(&bv, ONE); } else { bitvec_set_bit(&bv, ZERO); } /* support measurement capability */ if (sup->meas_cap) { bitvec_set_bit(&bv, ONE); bitvec_set_uint(&bv, sup->sms_val, 4); bitvec_set_uint(&bv, sup->sm_val, 4); } else { bitvec_set_bit(&bv, ZERO); } /* positioning method capability */ if (sup->loc_serv) { bitvec_set_bit(&bv, ONE); bitvec_set_bit(&bv, sup->e_otd_ass == 1); bitvec_set_bit(&bv, sup->e_otd_based == 1); bitvec_set_bit(&bv, sup->gps_ass == 1); bitvec_set_bit(&bv, sup->gps_based == 1); bitvec_set_bit(&bv, sup->gps_conv == 1); } else { bitvec_set_bit(&bv, ZERO); } /* The following bits are described in TS 24.008 */ /* EDGE multi slot support */ if (set->edge_ms_sup) { bitvec_set_bit(&bv, ONE); bitvec_set_uint(&bv, set->edge_ms_sup, 5); } else { bitvec_set_bit(&bv, ZERO); } /* EDGE support */ if (set->edge_psk_sup) { bitvec_set_bit(&bv, ONE); bitvec_set_bit(&bv, set->edge_psk_uplink == 1); if (set->p_gsm || (set->e_gsm || set->r_gsm)) { bitvec_set_bit(&bv, ONE); bitvec_set_uint(&bv, set->class_900_edge, 2); } else { bitvec_set_bit(&bv, ZERO); } if (set->dcs || set->pcs) { bitvec_set_bit(&bv, ONE); bitvec_set_uint(&bv, set->class_dcs_pcs_edge, 2); } else { bitvec_set_bit(&bv, ZERO); } } else { bitvec_set_bit(&bv, ZERO); } /* GSM 400 Bands */ if (set->gsm_480 || set->gsm_450) { bitvec_set_bit(&bv, ONE); bitvec_set_bit(&bv, set->gsm_480 == 1); bitvec_set_bit(&bv, set->gsm_450 == 1); bitvec_set_uint(&bv, set->class_400, 4); } else { bitvec_set_bit(&bv, ZERO); } /* GSM 850 Band */ if (set->gsm_850) { bitvec_set_bit(&bv, ONE); bitvec_set_uint(&bv, set->class_850, 4); } else { bitvec_set_bit(&bv, ZERO); } /* PCS Band */ if (set->pcs) { bitvec_set_bit(&bv, ONE); bitvec_set_uint(&bv, set->class_pcs, 4); } else { bitvec_set_bit(&bv, ZERO); } /* RAT Capability */ bitvec_set_bit(&bv, set->umts_fdd == 1); bitvec_set_bit(&bv, set->umts_tdd == 1); bitvec_set_bit(&bv, set->cdma_2000 == 1); /* DTM */ if (set->dtm) { bitvec_set_bit(&bv, ONE); bitvec_set_uint(&bv, set->class_dtm, 2); bitvec_set_bit(&bv, set->dtm_mac == 1); bitvec_set_bit(&bv, set->dtm_egprs == 1); } else { bitvec_set_bit(&bv, ZERO); } /* info: The max number of bits are about 80. */ /* partial bytes will be padded with zero */ *len = (bv.cur_bit + 7) >> 3; bitvec_fill(&bv, (*len*8) - bv.cur_bit, ZERO); return 0; } /* encode classmark 2 */ int gsm48_rr_enc_cm2(struct osmocom_ms *ms, struct gsm48_classmark2 *cm, uint16_t arfcn) { struct gsm_support *sup = &ms->support; struct gsm_settings *set = &ms->settings; cm->pwr_lev = gsm48_current_pwr_lev(set, arfcn); cm->a5_1 = !set->a5_1; cm->es_ind = sup->es_ind; cm->rev_lev = sup->rev_lev; cm->fc = (set->r_gsm || set->e_gsm); cm->vgcs = sup->vgcs; cm->vbs = sup->vbs; cm->sm_cap = set->sms_ptp; cm->ss_scr = sup->ss_ind; cm->ps_cap = sup->ps_cap; cm->a5_2 = set->a5_2; cm->a5_3 = set->a5_3; cm->cmsp = sup->cmsp; cm->solsa = sup->solsa; cm->lcsva_cap = sup->lcsva; return 0; } /* send classmark change */ static int gsm48_rr_tx_cm_change(struct osmocom_ms *ms) { struct gsm_support *sup = &ms->support; struct gsm_settings *set = &ms->settings; struct gsm48_rrlayer *rr = &ms->rrlayer; struct msgb *nmsg; struct gsm48_hdr *gh; struct gsm48_cm_change *cc; uint8_t cm3[14], *tlv; LOGP(DRR, LOGL_INFO, "CLASSMARK CHANGE\n"); nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); cc = (struct gsm48_cm_change *) msgb_put(nmsg, sizeof(*cc)); gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_CLSM_CHG; /* classmark 2 */ cc->cm2_len = sizeof(cc->cm2); gsm48_rr_enc_cm2(ms, &cc->cm2, rr->cd_now.arfcn); /* classmark 3 */ if (set->dcs || set->pcs || set->e_gsm || set->r_gsm || set->gsm_850 || set->a5_7 || set->a5_6 || set->a5_5 || set->a5_4 || sup->ms_sup || sup->ucs2_treat || sup->ext_meas || sup->meas_cap || sup->loc_serv) { cc->cm2.cm3 = 1; cm3[0] = GSM48_IE_CLASSMARK3; gsm48_rr_enc_cm3(ms, cm3 + 2, &cm3[1]); tlv = msgb_put(nmsg, 2 + cm3[1]); memcpy(tlv, cm3, 2 + cm3[1]); } return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg, 0); } /* receiving classmark enquiry */ static int gsm48_rr_rx_cm_enq(struct osmocom_ms *ms, struct msgb *msg) { /* send classmark */ return gsm48_rr_tx_cm_change(ms); } /* * 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; /* */ 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 */ 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 */ /* start random access */ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging, int paging_mi_type) { struct gsm_settings *set = &ms->settings; struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = cs->si; struct msgb *nmsg; struct gsm48_rr_hdr *nrrh; uint8_t chan_req_val, chan_req_mask; int rc; LOGP(DSUM, LOGL_INFO, "Establish radio link due to %s request\n", (paging) ? "paging" : "mobility management"); /* ignore paging, if not camping */ if (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; } /* ignore channel request while not camping on a cell */ if (!cs->selected) { LOGP(DRR, LOGL_INFO, "Channel request rejected, we did not " "properly select the serving cell.\n"); goto rel_ind; } /* 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) { if (paging) return rc; LOGP(DRR, LOGL_INFO, "Failed to leave IDLE mode.\n"); goto undefined; } /* 3.3.1.1.2 */ new_rr_state(rr, GSM48_RR_ST_CONN_PEND); /* set assignment state */ rr->wait_assign = 0; /* number of retransmissions (with first transmission) */ rr->n_chan_req = s->max_retrans + 1; /* generate CHAN REQ (9.1.8) */ switch (cause) { case RR_EST_CAUSE_EMERGENCY: /* 101xxxxx */ chan_req_mask = 0x1f; chan_req_val = 0xa0; LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Emergency call)\n", chan_req_val); break; case RR_EST_CAUSE_REESTAB_TCH_F: chan_req_mask = 0x1f; chan_req_val = 0xc0; LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (re-establish " "TCH/F)\n", chan_req_val); break; case RR_EST_CAUSE_REESTAB_TCH_H: if (s->neci) { chan_req_mask = 0x03; chan_req_val = 0x68; LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x " "(re-establish TCH/H with NECI)\n", chan_req_val); } else { chan_req_mask = 0x1f; chan_req_val = 0xc0; LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x " "(re-establish TCH/H no NECI)\n", chan_req_val); } break; case RR_EST_CAUSE_REESTAB_2_TCH_H: if (s->neci) { chan_req_mask = 0x03; chan_req_val = 0x6c; LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x " "(re-establish TCH/H+TCH/H with NECI)\n", chan_req_val); } else { chan_req_mask = 0x1f; chan_req_val = 0xc0; LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x " "(re-establish TCH/H+TCH/H no NECI)\n", chan_req_val); } break; case RR_EST_CAUSE_ANS_PAG_ANY: chan_req_mask = 0x1f; chan_req_val = 0x80; LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING " "Any channel)\n", chan_req_val); break; case RR_EST_CAUSE_ANS_PAG_SDCCH: chan_req_mask = 0x0f; chan_req_val = 0x10; LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING SDCCH)\n", chan_req_val); break; case RR_EST_CAUSE_ANS_PAG_TCH_F: switch (set->ch_cap) { case GSM_CAP_SDCCH: chan_req_mask = 0x0f; chan_req_val = 0x10; break; case GSM_CAP_SDCCH_TCHF: chan_req_mask = 0x1f; chan_req_val = 0x80; break; default: chan_req_mask = 0x0f; chan_req_val = 0x20; break; } LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING TCH/F)\n", chan_req_val); break; case RR_EST_CAUSE_ANS_PAG_TCH_ANY: switch (set->ch_cap) { case GSM_CAP_SDCCH: chan_req_mask = 0x0f; chan_req_val = 0x10; break; case GSM_CAP_SDCCH_TCHF: chan_req_mask = 0x1f; chan_req_val = 0x80; break; default: chan_req_mask = 0x0f; chan_req_val = 0x30; break; } LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING TCH/H or " "TCH/F)\n", chan_req_val); break; case RR_EST_CAUSE_ORIG_TCHF: /* ms supports no dual rate */ chan_req_mask = 0x1f; chan_req_val = 0xe0; LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Orig TCH/F)\n", chan_req_val); break; case RR_EST_CAUSE_LOC_UPD: if (s->neci) { chan_req_mask = 0x0f; chan_req_val = 0x00; LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Location " "Update with NECI)\n", chan_req_val); } else { chan_req_mask = 0x1f; chan_req_val = 0x00; LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Location " "Update no NECI)\n", chan_req_val); } break; case RR_EST_CAUSE_OTHER_SDCCH: if (s->neci) { chan_req_mask = 0x0f; chan_req_val = 0x10; LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (OTHER " "with NECI)\n", chan_req_val); } else { chan_req_mask = 0x1f; chan_req_val = 0xe0; LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (OTHER " "no NECI)\n", chan_req_val); } break; default: if (!rr->rr_est_req) /* no request from MM */ return -EINVAL; LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: with unknown " "establishment cause: %d\n", cause); undefined: LOGP(DSUM, LOGL_INFO, "Requesting channel failed\n"); rel_ind: nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); if (!nmsg) return -ENOMEM; nrrh = (struct gsm48_rr_hdr *)nmsg->data; nrrh->cause = RR_REL_CAUSE_UNDEFINED; gsm48_rr_upmsg(ms, nmsg); new_rr_state(rr, GSM48_RR_ST_IDLE); return -EINVAL; } /* store value, mask and clear history */ rr->chan_req_val = chan_req_val; rr->chan_req_mask = chan_req_mask; memset(rr->cr_hist, 0, sizeof(rr->cr_hist)); /* store establishment cause, so 'choose cell' selects the last cell * after location updating */ rr->est_cause = cause; /* store paging mobile identity type, if we respond to paging */ rr->paging_mi_type = paging_mi_type; /* if channel is already active somehow */ if (cs->ccch_state == GSM322_CCCH_ST_DATA) return gsm48_rr_tx_rand_acc(ms, NULL); return 0; } /* send first/next channel request in conn pend state */ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = &ms->cellsel.sel_si; struct gsm_settings *set = &ms->settings; struct msgb *nmsg; struct abis_rsl_cchan_hdr *ncch; int slots; uint8_t chan_req; uint8_t tx_power; enum gsm_band 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) return 0; /* 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; } /* shift history and store */ 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; 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 (cs->ccch_state != GSM322_CCCH_ST_DATA) { LOGP(DRR, LOGL_INFO, "CCCH channel activation failed.\n"); fail: if (rr->rr_est_req) { struct msgb *msg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); struct gsm48_rr_hdr *rrh; LOGP(DSUM, LOGL_INFO, "Requesting channel failed\n"); if (!msg) return -ENOMEM; rrh = (struct gsm48_rr_hdr *)msg->data; rrh->cause = RR_REL_CAUSE_RA_FAILURE; gsm48_rr_upmsg(ms, msg); } new_rr_state(rr, GSM48_RR_ST_IDLE); return 0; } if (!s || !s->si3 || !s->tx_integer) { LOGP(DRR, LOGL_NOTICE, "Not enough SYSINFO\n"); goto fail; } if (rr->state == GSM48_RR_ST_IDLE) { LOGP(DRR, LOGL_INFO, "MM already released RR.\n"); return 0; } LOGP(DRR, LOGL_INFO, "RANDOM ACCESS (requests left %d)\n", rr->n_chan_req); if (!rr->n_chan_req) { LOGP(DRR, LOGL_INFO, "Done with sending RANDOM ACCESS " "bursts\n"); if (!osmo_timer_pending(&rr->t3126)) start_rr_t3126(rr, 5, 0); /* TODO improve! */ return 0; } rr->n_chan_req--; if (rr->wait_assign == 0) { /* first random access, without delay of slots */ slots = 0; rr->wait_assign = 1; } else { /* 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 */ slots = 55; else slots = 41; break; case 4: case 9: case 16: if (s->ccch_conf != 1) slots = 76; else slots = 52; break; case 5: case 10: case 20: if (s->ccch_conf != 1) slots = 109; else slots = 58; break; case 6: case 11: case 25: if (s->ccch_conf != 1) slots = 163; else slots = 86; break; default: if (s->ccch_conf != 1) slots = 217; else slots = 115; break; } } chan_req = layer23_random(); chan_req &= rr->chan_req_mask; chan_req |= rr->chan_req_val; LOGP(DRR, LOGL_INFO, "RANDOM ACCESS (Tx-integer %d combined %s " "S(lots) %d ra 0x%02x)\n", s->tx_integer, (s->ccch_conf == 1) ? "yes": "no", slots, chan_req); slots = (random() % s->tx_integer) + slots; /* (re)send CHANNEL RQD with new randiom */ nmsg = gsm48_rsl_msgb_alloc(); if (!nmsg) return -ENOMEM; ncch = (struct abis_rsl_cchan_hdr *) msgb_put(nmsg, sizeof(*ncch) + 4 + 2 + 2); rsl_init_cchan_hdr(ncch, RSL_MT_CHAN_RQD); ncch->chan_nr = RSL_CHAN_RACH; ncch->data[0] = RSL_IE_REQ_REFERENCE; ncch->data[1] = chan_req; ncch->data[2] = (slots >> 8) | ((s->ccch_conf == 1) << 7); ncch->data[3] = slots; ncch->data[4] = RSL_IE_ACCESS_DELAY; ncch->data[5] = set->alter_delay; /* (-)=earlier (+)=later */ ncch->data[6] = RSL_IE_MS_POWER; if (set->alter_tx_power) { tx_power = set->alter_tx_power_value; LOGP(DRR, LOGL_INFO, "Use alternative tx-power %d (%d dBm)\n", tx_power, ms_pwr_dbm(band, tx_power)); } else { tx_power = s->ms_txpwr_max_cch; /* power offset in case of DCS1800 */ if (s->po && (cs->arfcn & 1023) >= 512 && (cs->arfcn & 1023) <= 885) { LOGP(DRR, LOGL_INFO, "Use MS-TXPWR-MAX-CCH power value " "%d (%d dBm) with offset %d dBm\n", tx_power, ms_pwr_dbm(band, tx_power), s->po_value * 2); /* use reserved bits 7,8 for offset (+ X * 2dB) */ tx_power |= s->po_value << 6; } else LOGP(DRR, LOGL_INFO, "Use MS-TXPWR-MAX-CCH power value " "%d (%d dBm)\n", tx_power, ms_pwr_dbm(band, tx_power)); } ncch->data[7] = tx_power; /* set initial indications */ rr->cd_now.ind_tx_power = s->ms_txpwr_max_cch; rr->cd_now.ind_ta = set->alter_delay; /* store ra until confirmed, then copy it with time into cr_hist */ rr->cr_ra = chan_req; return lapdm_rslms_recvmsg(nmsg, &ms->lapdm_channel); } /* * system information */ /* send sysinfo event to other layers */ static int gsm48_new_sysinfo(struct osmocom_ms *ms, uint8_t type) { struct gsm48_sysinfo *s = ms->cellsel.si; struct gsm322_cellsel *cs = &ms->cellsel; struct msgb *nmsg; struct gsm322_msg *em; /* update list of measurements, if BA(SACCH) is complete and new */ if (s && (type == GSM48_MT_RR_SYSINFO_5 || type == GSM48_MT_RR_SYSINFO_5bis || type == GSM48_MT_RR_SYSINFO_5ter) && s->si5 && (!s->nb_ext_ind_si5 || s->si5bis)) { struct gsm48_rr_meas *rrmeas = &ms->rrlayer.meas; 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 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 = 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 */ nmsg = gsm322_msgb_alloc(GSM322_EVENT_SYSINFO); if (!nmsg) return -ENOMEM; em = (struct gsm322_msg *) nmsg->data; em->sysinfo = type; gsm322_cs_sendmsg(ms, nmsg); /* if not camping, we don't care about SI */ if (ms->cellsel.neighbour || (ms->cellsel.state != GSM322_C3_CAMPED_NORMALLY && ms->cellsel.state != GSM322_C7_CAMPED_ANY_CELL)) return 0; /* send timer info to location update process */ nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_SYSINFO); if (!nmsg) return -ENOMEM; gsm48_mmevent_msg(ms, nmsg); return 0; } /* receive "SYSTEM INFORMATION 1" message (9.1.31) */ static int gsm48_rr_rx_sysinfo1(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_system_information_type_1 *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 1 " "ignored\n"); return -EINVAL; } if (payload_len < 0) { LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 1 " "message.\n"); return -EINVAL; } 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)); LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 1\n"); return gsm48_new_sysinfo(ms, si->header.system_information); } /* receive "SYSTEM INFORMATION 2" message (9.1.32) */ static int gsm48_rr_rx_sysinfo2(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_system_information_type_2 *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 2 " "ignored\n"); return -EINVAL; } if (payload_len < 0) { LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 2 " "message.\n"); return -EINVAL; } 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)); LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2\n"); return gsm48_new_sysinfo(ms, si->header.system_information); } /* receive "SYSTEM INFORMATION 2bis" message (9.1.33) */ static int gsm48_rr_rx_sysinfo2bis(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_system_information_type_2bis *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 2bis" " ignored\n"); return -EINVAL; } if (payload_len < 0) { LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 2bis " "message.\n"); return -EINVAL; } 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)); LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2bis\n"); return gsm48_new_sysinfo(ms, si->header.system_information); } /* receive "SYSTEM INFORMATION 2ter" message (9.1.34) */ static int gsm48_rr_rx_sysinfo2ter(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_system_information_type_2ter *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 2ter" " ignored\n"); return -EINVAL; } if (payload_len < 0) { LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 2ter " "message.\n"); return -EINVAL; } 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)); LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2ter\n"); return gsm48_new_sysinfo(ms, si->header.system_information); } /* receive "SYSTEM INFORMATION 3" message (9.1.35) */ static int gsm48_rr_rx_sysinfo3(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_system_information_type_3 *si = msgb_l3(msg); struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = cs->si; int payload_len = msgb_l3len(msg) - sizeof(*si); if (!s) { LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 3 " "ignored\n"); return -EINVAL; } if (payload_len < 0) { LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 3 " "message.\n"); return -EINVAL; } 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)); if (cs->ccch_mode == CCCH_MODE_NONE) { cs->ccch_mode = (s->ccch_conf == 1) ? CCCH_MODE_COMBINED : CCCH_MODE_NON_COMBINED; LOGP(DRR, LOGL_NOTICE, "Changing CCCH_MODE to %d\n", cs->ccch_mode); l1ctl_tx_ccch_mode_req(ms, cs->ccch_mode); } return gsm48_new_sysinfo(ms, si->header.system_information); } /* receive "SYSTEM INFORMATION 4" message (9.1.36) */ static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_system_information_type_4 *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 4 " "ignored\n"); return -EINVAL; } if (payload_len < 0) { LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 4 " "message.\n"); return -EINVAL; } 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 (lai=%s)\n", osmo_lai_name(&s->lai)); return gsm48_new_sysinfo(ms, si->header.system_information); } /* receive "SYSTEM INFORMATION 5" message (9.1.37) */ static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *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) - 1; if (!s) { LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5 " "ignored\n"); return -EINVAL; } if (payload_len < 0) { LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 5 " "message.\n"); return -EINVAL; } 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)); LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5\n"); return gsm48_new_sysinfo(ms, si->system_information); } /* receive "SYSTEM INFORMATION 5bis" message (9.1.38) */ static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *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) - 1; if (!s) { LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5bis" " ignored\n"); return -EINVAL; } if (payload_len < 0) { LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 5bis " "message.\n"); return -EINVAL; } if (!memcmp(si, s->si5b_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si5b_msg)))) return 0; gsm48_decode_sysinfo5bis(s, si, msgb_l3len(msg)); LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5bis\n"); return gsm48_new_sysinfo(ms, si->system_information); } /* receive "SYSTEM INFORMATION 5ter" message (9.1.39) */ static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *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) - 1; if (!s) { LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5ter" " ignored\n"); return -EINVAL; } if (payload_len < 0) { LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 5ter " "message.\n"); return -EINVAL; } if (!memcmp(si, s->si5t_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si5t_msg)))) return 0; gsm48_decode_sysinfo5ter(s, si, msgb_l3len(msg)); LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5ter\n"); return gsm48_new_sysinfo(ms, si->system_information); } /* receive "SYSTEM INFORMATION 6" message (9.1.39) */ static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *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) - 1; if (!s) { LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 6 " "ignored\n"); return -EINVAL; } if (payload_len < 0) { LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 6 " "message.\n"); return -EINVAL; } 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 (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); 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 */ /* paging channel request */ static int gsm48_rr_chan2cause[4] = { RR_EST_CAUSE_ANS_PAG_ANY, RR_EST_CAUSE_ANS_PAG_SDCCH, RR_EST_CAUSE_ANS_PAG_TCH_F, RR_EST_CAUSE_ANS_PAG_TCH_ANY }; /* 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; struct osmo_mobile_identity mi; char buf[32]; int rc; 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 ((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, " %s (not for us)\n", buf); break; case GSM_MI_TYPE_IMSI: if (!strcmp(mi.imsi, ms->subscr.imsi)) { LOGP(DPAG, LOGL_INFO, " %s matches\n", buf); return mi.type; } else 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); } return 0; } /* 9.1.22 PAGING REQUEST 1 message received */ static int gsm48_rr_rx_pag_req_1(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_paging1 *pa = msgb_l3(msg); int payload_len = msgb_l3len(msg) - sizeof(*pa); int chan_1, chan_2; uint8_t *mi, mi_type; /* empty paging request */ if (payload_len >= 2 && (pa->data[1] & GSM_MI_TYPE_MASK) == 0) return 0; /* 3.3.1.1.2: ignore paging while not camping on a cell */ if (rr->state != GSM48_RR_ST_IDLE || !cs->selected || (cs->state != GSM322_C3_CAMPED_NORMALLY && cs->state != GSM322_C7_CAMPED_ANY_CELL) || cs->neighbour) { LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n"); return 0; } LOGP(DPAG, LOGL_INFO, "PAGING REQUEST 1\n"); if (payload_len < 2) { short_read: LOGP(DRR, LOGL_NOTICE, "Short read of PAGING REQUEST 1 " "message.\n"); return -EINVAL; } /* channel needed */ chan_1 = pa->cneed1; chan_2 = pa->cneed2; /* first MI */ mi = pa->data; if (payload_len < mi[0] + 1) goto short_read; if ((mi_type = gsm_match_mi(ms, mi)) > 0) return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1, mi_type); /* second MI */ payload_len -= mi[0] + 1; mi = pa->data + mi[0] + 1; if (payload_len < 2) return 0; if (mi[0] != GSM48_IE_MOBILE_ID) return 0; if (payload_len < mi[1] + 2) goto short_read; if ((mi_type = gsm_match_mi(ms, mi + 1)) > 0) return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1, mi_type); return 0; } /* 9.1.23 PAGING REQUEST 2 message received */ static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_paging2 *pa = msgb_l3(msg); int payload_len = msgb_l3len(msg) - sizeof(*pa); uint8_t *mi, mi_type; int chan_1, chan_2, chan_3; /* 3.3.1.1.2: ignore paging while not camping on a cell */ if (rr->state != GSM48_RR_ST_IDLE || !cs->selected || (cs->state != GSM322_C3_CAMPED_NORMALLY && cs->state != GSM322_C7_CAMPED_ANY_CELL) || cs->neighbour) { LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n"); return 0; } LOGP(DPAG, LOGL_INFO, "PAGING REQUEST 2\n"); if (payload_len < 0) { short_read: LOGP(DRR, LOGL_NOTICE, "Short read of PAGING REQUEST 2 " "message .\n"); return -EINVAL; } /* channel needed */ chan_1 = pa->cneed1; chan_2 = pa->cneed2; /* first MI */ if (ms->subscr.tmsi == ntohl(pa->tmsi1) && (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); } else LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n", ntohl(pa->tmsi1)); /* second MI */ if (ms->subscr.tmsi == ntohl(pa->tmsi2) && (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)); /* third MI */ mi = pa->data; if (payload_len < 2) return 0; if (mi[0] != GSM48_IE_MOBILE_ID) return 0; if (payload_len < mi[1] + 2 + 1) /* must include "channel needed" */ goto short_read; chan_3 = mi[mi[1] + 2] & 0x03; /* channel needed */ if ((mi_type = gsm_match_mi(ms, mi + 1)) > 0) return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_3], 1, mi_type); return 0; } /* 9.1.24 PAGING REQUEST 3 message received */ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_paging3 *pa = msgb_l3(msg); int payload_len = msgb_l3len(msg) - sizeof(*pa); int chan_1, chan_2, chan_3, chan_4; /* 3.3.1.1.2: ignore paging while not camping on a cell */ if (rr->state != GSM48_RR_ST_IDLE || !cs->selected || (cs->state != GSM322_C3_CAMPED_NORMALLY && cs->state != GSM322_C7_CAMPED_ANY_CELL) || cs->neighbour) { LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n"); return 0; } LOGP(DPAG, LOGL_INFO, "PAGING REQUEST 3\n"); if (payload_len < 0) { /* must include "channel needed", part of *pa */ LOGP(DRR, LOGL_NOTICE, "Short read of PAGING REQUEST 3 " "message .\n"); return -EINVAL; } /* channel needed */ chan_1 = pa->cneed1; chan_2 = pa->cneed2; chan_3 = pa->cneed3; chan_4 = pa->cneed4; /* first MI */ if (ms->subscr.tmsi == ntohl(pa->tmsi1) && (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); } else LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n", ntohl(pa->tmsi1)); /* second MI */ if (ms->subscr.tmsi == ntohl(pa->tmsi2) && (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)); /* third MI */ if (ms->subscr.tmsi == ntohl(pa->tmsi3) && (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); } else LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n", ntohl(pa->tmsi3)); /* fourth MI */ if (ms->subscr.tmsi == ntohl(pa->tmsi4) && (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); } else LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n", ntohl(pa->tmsi4)); return 0; } /* * (immediate) assignment */ /* 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 < 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; 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 " "(fn=%d,%d,%d)\n", ref->ra, ia_t1, ia_t2, ia_t3); return 1; } else LOGP(DRR, LOGL_INFO, "request %02x matches " "but not frame number (IMM.ASS " "fn=%d,%d,%d != RACH fn=%d,%d,%d)\n", ref->ra, ia_t1, ia_t2, ia_t3, cr_t1, cr_t2, cr_t3); } } return 0; } /* 9.1.18 IMMEDIATE ASSIGNMENT is received */ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm48_imm_ass *ia = msgb_l3(msg); struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = cs->si; int ma_len = msgb_l3len(msg) - sizeof(*ia); uint8_t ch_type, ch_subch, ch_ts; struct gsm48_rr_cd cd; #ifndef TEST_STARTING_TIMER uint8_t *st, st_len; #endif /* ignore imm.ass. while not camping on a cell */ if (!cs->selected || cs->neighbour || !s) { LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT ignored, we are " "have not proper selected the serving cell.\n"); return 0; } memset(&cd, 0, sizeof(cd)); cd.ind_tx_power = rr->cd_now.ind_tx_power; if (ma_len < 0 /* mobile allocation IE must be included */ || ia->mob_alloc_len > ma_len) { /* short read of IE */ LOGP(DRR, LOGL_NOTICE, "Short read of IMMEDIATE ASSIGNMENT " "message.\n"); return -EINVAL; } if (ia->mob_alloc_len > 8) { LOGP(DRR, LOGL_NOTICE, "Moble allocation in IMMEDIATE " "ASSIGNMENT too large.\n"); return -EINVAL; } /* starting time */ #ifdef TEST_STARTING_TIMER cd.start = 1; cd.start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432; LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n"); #else st_len = ma_len - ia->mob_alloc_len; st = ia->mob_alloc + ia->mob_alloc_len; if (st_len >= 3 && st[0] == GSM48_IE_START_TIME) gsm48_decode_start_time(&cd, (struct gsm48_start_time *)(st+1)); #endif /* decode channel description */ LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT:\n"); cd.chan_nr = ia->chan_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); return -EINVAL; } if (ia->chan_desc.h0.h) { cd.h = 1; gsm48_decode_chan_h1(&ia->chan_desc, &cd.tsc, &cd.maio, &cd.hsn); LOGP(DRR, LOGL_INFO, " (ta %d/%dm ra 0x%02x chan_nr 0x%02x " "MAIO %u HSN %u TS %u SS %u TSC %u)\n", ia->timing_advance, ia->timing_advance * GSM_TA_CM / 100, ia->req_ref.ra, ia->chan_desc.chan_nr, cd.maio, cd.hsn, ch_ts, ch_subch, cd.tsc); } else { cd.h = 0; gsm48_decode_chan_h0(&ia->chan_desc, &cd.tsc, &cd.arfcn); 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, ia->timing_advance * GSM_TA_CM / 100, ia->req_ref.ra, ia->chan_desc.chan_nr, gsm_print_arfcn(cd.arfcn), ch_ts, ch_subch, cd.tsc); } /* 3.3.1.1.2: ignore assignment while idle */ if (rr->state != GSM48_RR_ST_CONN_PEND || rr->wait_assign == 0) { LOGP(DRR, LOGL_INFO, "Not for us, no request.\n"); return 0; } if (rr->wait_assign == 2) { LOGP(DRR, LOGL_INFO, "Ignoring, channel already assigned.\n"); return 0; } /* request 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 */ rr->cd_now.ind_ta = ia->timing_advance; /* mobile allocation */ memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len, ia->mob_alloc_len + 1); rr->wait_assign = 2; /* reset scheduler */ LOGP(DRR, LOGL_INFO, "resetting scheduler\n"); l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED); return gsm48_rr_dl_est(ms); } LOGP(DRR, LOGL_INFO, "Request, but not for us.\n"); return 0; } /* 9.1.19 IMMEDIATE ASSIGNMENT EXTENDED is received */ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = cs->si; struct gsm48_imm_ass_ext *ia = msgb_l3(msg); int ma_len = msgb_l3len(msg) - sizeof(*ia); uint8_t ch_type, ch_subch, ch_ts; struct gsm48_rr_cd cd1, cd2; #ifndef TEST_STARTING_TIMER uint8_t *st, st_len; #endif /* ignore imm.ass.ext while not camping on a cell */ if (!cs->selected || cs->neighbour || !s) { LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT ignored, we are " "have not proper selected the serving cell.\n"); return 0; } memset(&cd1, 0, sizeof(cd1)); cd1.ind_tx_power = rr->cd_now.ind_tx_power; memset(&cd2, 0, sizeof(cd2)); cd2.ind_tx_power = rr->cd_now.ind_tx_power; if (ma_len < 0 /* mobile allocation IE must be included */ || ia->mob_alloc_len > ma_len) { /* short read of IE */ LOGP(DRR, LOGL_NOTICE, "Short read of IMMEDIATE ASSIGNMENT " "EXTENDED message.\n"); return -EINVAL; } if (ia->mob_alloc_len > 4) { LOGP(DRR, LOGL_NOTICE, "Moble allocation in IMMEDIATE " "ASSIGNMENT EXTENDED too large.\n"); return -EINVAL; } #ifdef TEST_STARTING_TIMER cd1.start = 1; cd2.start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432; memcpy(&cd2, &cd1, sizeof(cd2)); LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n"); #else /* starting time */ st_len = ma_len - ia->mob_alloc_len; st = ia->mob_alloc + ia->mob_alloc_len; if (st_len >= 3 && st[0] == GSM48_IE_START_TIME) { gsm48_decode_start_time(&cd1, (struct gsm48_start_time *)(st+1)); memcpy(&cd2, &cd1, sizeof(cd2)); } #endif /* decode channel description */ LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT EXTENDED:\n"); cd1.chan_nr = ia->chan_desc1.chan_nr; 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, &cd1.hsn); LOGP(DRR, LOGL_INFO, " assignment 1 (ta %d/%dm ra 0x%02x " "chan_nr 0x%02x MAIO %u HSN %u TS %u SS %u TSC %u)\n", ia->timing_advance1, ia->timing_advance1 * GSM_TA_CM / 100, ia->req_ref1.ra, ia->chan_desc1.chan_nr, cd1.maio, cd1.hsn, ch_ts, ch_subch, cd1.tsc); } else { cd1.h = 0; gsm48_decode_chan_h0(&ia->chan_desc1, &cd1.tsc, &cd1.arfcn); 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, ia->timing_advance1 * GSM_TA_CM / 100, ia->req_ref1.ra, ia->chan_desc1.chan_nr, gsm_print_arfcn(cd1.arfcn), ch_ts, ch_subch, cd1.tsc); } cd2.chan_nr = ia->chan_desc2.chan_nr; 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, &cd2.hsn); LOGP(DRR, LOGL_INFO, " assignment 2 (ta %d/%dm ra 0x%02x " "chan_nr 0x%02x MAIO %u HSN %u TS %u SS %u TSC %u)\n", ia->timing_advance2, ia->timing_advance2 * GSM_TA_CM / 100, ia->req_ref2.ra, ia->chan_desc2.chan_nr, cd2.maio, cd2.hsn, ch_ts, ch_subch, cd2.tsc); } else { cd2.h = 0; gsm48_decode_chan_h0(&ia->chan_desc2, &cd2.tsc, &cd2.arfcn); 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, ia->timing_advance2 * GSM_TA_CM / 100, ia->req_ref2.ra, ia->chan_desc2.chan_nr, gsm_print_arfcn(cd2.arfcn), ch_ts, ch_subch, cd2.tsc); } /* 3.3.1.1.2: ignore assignment while idle */ if (rr->state != GSM48_RR_ST_CONN_PEND || rr->wait_assign == 0) { LOGP(DRR, LOGL_INFO, "Not for us, no request.\n"); return 0; } if (rr->wait_assign == 2) { LOGP(DRR, LOGL_INFO, "Ignoring, channel already assigned.\n"); return 0; } /* request ref 1 */ if (gsm48_match_ra(ms, &ia->req_ref1, IMM_ASS_HISTORY)) { /* channel description */ memcpy(&rr->cd_now, &cd1, sizeof(rr->cd_now)); /* timing advance */ rr->cd_now.ind_ta = ia->timing_advance1; /* mobile allocation */ memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len, ia->mob_alloc_len + 1); rr->wait_assign = 2; /* reset scheduler */ LOGP(DRR, LOGL_INFO, "resetting scheduler\n"); l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED); return gsm48_rr_dl_est(ms); } /* request ref 2 */ if (gsm48_match_ra(ms, &ia->req_ref2, IMM_ASS_HISTORY)) { /* channel description */ memcpy(&rr->cd_now, &cd2, sizeof(rr->cd_now)); /* timing advance */ rr->cd_now.ind_ta = ia->timing_advance2; /* mobile allocation */ memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len, ia->mob_alloc_len + 1); rr->wait_assign = 2; /* reset scheduler */ LOGP(DRR, LOGL_INFO, "resetting scheduler\n"); l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED); return gsm48_rr_dl_est(ms); } LOGP(DRR, LOGL_INFO, "Request, but not for us.\n"); return 0; } /* 9.1.20 IMMEDIATE ASSIGNMENT REJECT is received */ static int gsm48_rr_rx_imm_ass_rej(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm48_imm_ass_rej *ia = msgb_l3(msg); int payload_len = msgb_l3len(msg) - sizeof(*ia); int i; struct gsm48_req_ref *req_ref; uint8_t t3122_value; /* 3.3.1.1.2: ignore assignment while idle */ if (rr->state != GSM48_RR_ST_CONN_PEND || rr->wait_assign == 0) return 0; if (rr->wait_assign == 2) { return 0; } if (payload_len < 0) { LOGP(DRR, LOGL_NOTICE, "Short read of IMMEDIATE ASSIGNMENT " "REJECT message.\n"); return -EINVAL; } for (i = 0; i < 4; i++) { /* request reference */ req_ref = (struct gsm48_req_ref *) (((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, IMM_ASS_HISTORY)) { /* wait indication */ t3122_value = *(((uint8_t *)&ia->wait_ind1) + i * 4); if (t3122_value) start_rr_t3122(rr, t3122_value, 0); /* start timer 3126 if not already */ if (!osmo_timer_pending(&rr->t3126)) start_rr_t3126(rr, 5, 0); /* TODO improve! */ /* stop assignment requests */ rr->n_chan_req = 0; /* wait until timer 3126 expires, then release * or wait for channel assignment */ return 0; } } return 0; } /* 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); struct gsm48_add_ass *aa = (struct gsm48_add_ass *)gh->data; int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*aa); 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); } 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); } return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); } /* * measturement reports */ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm48_sysinfo *s = ms->cellsel.si; struct rx_meas_stat *meas = &rr->ms->meas; struct gsm48_rr_meas *rrmeas = &rr->meas; struct msgb *nmsg; struct gsm48_hdr *gh; struct gsm48_meas_res *mr; uint8_t serv_rxlev_full = 0, serv_rxlev_sub = 0, serv_rxqual_full = 0, serv_rxqual_sub = 0; uint8_t ta, tx_power; uint8_t rep_ba = 0, rep_valid = 0, meas_valid = 0; uint8_t n = 0, rxlev_nc[6], bsic_nc[6], bcch_f_nc[6]; /* just in case! */ if (!s) return -EINVAL; /* check if SI5* is completely received, check BA-IND */ if (s->si5 && (!s->nb_ext_ind_si5 || s->si5bis)) { rep_ba = s->nb_ba_ind_si5; 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 mismatch on SI5*"); } else rep_valid = 1; } /* check for valid measurements, any frame must exist */ if (meas->frames) { meas_valid = 1; serv_rxlev_full = serv_rxlev_sub = (meas->rxlev + (meas->frames / 2)) / meas->frames; serv_rxqual_full = serv_rxqual_sub = 0; // FIXME } memset(&rxlev_nc, 0, sizeof(rxlev_nc)); memset(&bsic_nc, 0, sizeof(bsic_nc)); memset(&bcch_f_nc, 0, sizeof(bcch_f_nc)); if (rep_valid) { int8_t strongest, current; uint8_t ncc; int i, index, strongest_i; #if 0 /* FIXME: multi-band reporting, if not: 0 = normal reporting */ uint8_t multi_rep = s->si5ter ? s->nb_multi_rep_si5ter : 0; #endif /* get 6 strongest measurements */ strongest = 127; /* infinite */ strongest_i = -1; /* make gcc happy */ for (n = 0; n < 6; n++) { 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) { current = rrmeas->nc_rxlev_dbm[i]; strongest = current; index = i; strongest_i = i; } } if (current == -128) /* no more found */ break; rxlev_nc[n] = rrmeas->nc_rxlev_dbm[index] + 110; bsic_nc[n] = rrmeas->nc_bsic[index]; bcch_f_nc[n] = index; } } nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; /* use indicated tx-power and TA (not the altered ones) */ tx_power = rr->cd_now.ind_tx_power; // FIXME: degrade power to the max supported level ta = rr->cd_now.ind_ta; gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); mr = (struct gsm48_meas_res *) msgb_put(nmsg, sizeof(*mr)); gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_MEAS_REP; /* measurement results */ mr->rxlev_full = serv_rxlev_full; mr->rxlev_sub = serv_rxlev_sub; mr->rxqual_full = serv_rxqual_full; mr->rxqual_sub = serv_rxqual_sub; mr->dtx_used = 0; // FIXME: no DTX yet mr->ba_used = rep_ba; mr->meas_valid = !meas_valid; /* 0 = valid */ if (rep_valid) { mr->no_nc_n_hi = n >> 2; mr->no_nc_n_lo = n & 3; } else { /* no results for serving cells */ mr->no_nc_n_hi = 1; mr->no_nc_n_lo = 3; } mr->rxlev_nc1 = rxlev_nc[0]; mr->rxlev_nc2_hi = rxlev_nc[1] >> 1; mr->rxlev_nc2_lo = rxlev_nc[1] & 1; mr->rxlev_nc3_hi = rxlev_nc[2] >> 2; mr->rxlev_nc3_lo = rxlev_nc[2] & 3; mr->rxlev_nc4_hi = rxlev_nc[3] >> 3; mr->rxlev_nc4_lo = rxlev_nc[3] & 7; mr->rxlev_nc5_hi = rxlev_nc[4] >> 4; mr->rxlev_nc5_lo = rxlev_nc[4] & 15; mr->rxlev_nc6_hi = rxlev_nc[5] >> 5; mr->rxlev_nc6_lo = rxlev_nc[5] & 31; mr->bsic_nc1_hi = bsic_nc[0] >> 3; mr->bsic_nc1_lo = bsic_nc[0] & 7; mr->bsic_nc2_hi = bsic_nc[1] >> 4; mr->bsic_nc2_lo = bsic_nc[1] & 15; mr->bsic_nc3_hi = bsic_nc[2] >> 5; mr->bsic_nc3_lo = bsic_nc[2] & 31; mr->bsic_nc4 = bsic_nc[3]; mr->bsic_nc5 = bsic_nc[4]; mr->bsic_nc6 = bsic_nc[5]; mr->bcch_f_nc1 = bcch_f_nc[0]; mr->bcch_f_nc2 = bcch_f_nc[1]; mr->bcch_f_nc3 = bcch_f_nc[2]; mr->bcch_f_nc4 = bcch_f_nc[3]; mr->bcch_f_nc5_hi = bcch_f_nc[4] >> 1; mr->bcch_f_nc5_lo = bcch_f_nc[4] & 1; mr->bcch_f_nc6_hi = bcch_f_nc[5] >> 2; mr->bcch_f_nc6_lo = bcch_f_nc[5] & 3; LOGP(DRR, LOGL_INFO, "MEAS REP: pwr=%d TA=%d meas-invalid=%d " "rxlev-full=%d rxlev-sub=%d rxqual-full=%d rxqual-sub=%d " "dtx %d ba %d no-ncell-n %d\n", tx_power, ta, mr->meas_valid, mr->rxlev_full - 110, mr->rxlev_sub - 110, mr->rxqual_full, mr->rxqual_sub, mr->dtx_used, mr->ba_used, (mr->no_nc_n_hi << 2) | mr->no_nc_n_lo); msgb_tv16_push(nmsg, RSL_IE_L3_INFO, nmsg->tail - (uint8_t *)msgb_l3(nmsg)); msgb_push(nmsg, 2 + 2); nmsg->data[0] = RSL_IE_TIMING_ADVANCE; nmsg->data[1] = ta; nmsg->data[2] = RSL_IE_MS_POWER; nmsg->data[3] = tx_power; rsl_rll_push_hdr(nmsg, RSL_MT_UNIT_DATA_REQ, rr->cd_now.chan_nr, 0x40, 1); return lapdm_rslms_recvmsg(nmsg, &ms->lapdm_channel); } /* * link establishment and release */ /* process "Loss Of Signal" */ int gsm48_rr_los(struct osmocom_ms *ms) { struct gsm48_rrlayer *rr = &ms->rrlayer; uint8_t *mode; struct msgb *nmsg; struct gsm48_rr_hdr *nrrh; LOGP(DSUM, LOGL_INFO, "Radio link lost signal\n"); if (rr->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); switch(rr->state) { case GSM48_RR_ST_CONN_PEND: LOGP(DRR, LOGL_INFO, "LOS during RACH request\n"); /* stop pending RACH timer */ stop_rr_t3126(rr); break; case GSM48_RR_ST_DEDICATED: LOGP(DRR, LOGL_INFO, "LOS during dedicated mode, release " "locally\n"); new_rr_state(rr, GSM48_RR_ST_REL_PEND); /* release message */ rel_local: nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; mode = msgb_put(nmsg, 2); mode[0] = RSL_IE_RELEASE_MODE; mode[1] = 1; /* local release */ /* start release */ gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0); /* release SAPI 3 link, if exits */ gsm48_release_sapi3_link(ms); return 0; case GSM48_RR_ST_REL_PEND: LOGP(DRR, LOGL_INFO, "LOS during RR release procedure, release " "locally\n"); /* stop pending RACH timer */ stop_rr_t3110(rr); /* release locally */ goto rel_local; default: /* this should not happen */ LOGP(DRR, LOGL_ERROR, "LOS in IDLE state, ignoring\n"); return -EINVAL; } /* send inication to upper layer */ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); if (!nmsg) return -ENOMEM; nrrh = (struct gsm48_rr_hdr *)nmsg->data; nrrh->cause = RR_REL_CAUSE_LOST_SIGNAL; gsm48_rr_upmsg(ms, nmsg); /* return idle */ new_rr_state(rr, GSM48_RR_ST_IDLE); return 0; } /* activation of channel in dedicated mode */ static int gsm48_rr_activate_channel(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t ma_len) { struct gsm_subscriber *subscr = &ms->subscr; struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm_settings *set = &ms->settings; struct gsm48_sysinfo *s = ms->cellsel.si; struct rx_meas_stat *meas = &ms->meas; uint8_t ch_type, ch_subch, ch_ts; uint8_t timeout = 64; /* setting (new) timing advance */ LOGP(DRR, LOGL_INFO, "setting indicated TA %d (actual TA %d)\n", cd->ind_ta, cd->ind_ta - set->alter_delay); l1ctl_tx_param_req(ms, cd->ind_ta - set->alter_delay, (set->alter_tx_power) ? set->alter_tx_power_value : cd->ind_tx_power); /* reset measurement and link timeout */ meas->ds_fail = 0; if (s) { if (s->sacch_radio_link_timeout) { timeout = s->sacch_radio_link_timeout; LOGP(DRR, LOGL_INFO, "using last SACCH timeout %d\n", timeout); } else if (s->bcch_radio_link_timeout) { timeout = s->bcch_radio_link_timeout; LOGP(DRR, LOGL_INFO, "using last BCCH timeout %d\n", timeout); } } meas->rl_fail = meas->s = timeout; /* setting initial (invalid) measurement report, resetting SI5* */ if (s) { memset(s->si5_msg, 0, sizeof(s->si5_msg)); memset(s->si5b_msg, 0, sizeof(s->si5b_msg)); memset(s->si5t_msg, 0, sizeof(s->si5t_msg)); } meas->frames = meas->snr = meas->berr = meas->rxlev = 0; rr->meas.nc_num = 0; stop_rr_t_meas(rr); start_rr_t_meas(rr, 1, 0); gsm48_rr_tx_meas_rep(ms); /* establish */ 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, cd->tch_flags); else 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 */ s->si5 = s->si5bis = s->si5ter = s->si6 = 0; if (rr->cipher_on) l1ctl_tx_crypto_req(ms, rr->cd_now.chan_nr, rr->cipher_type + 1, subscr->key, 8); return 0; } /* frequency change of channel "after time" */ static int gsm48_rr_channel_after_time(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t ma_len, uint16_t fn) { struct gsm_subscriber *subscr = &ms->subscr; struct gsm48_rrlayer *rr = &ms->rrlayer; if (cd->h) l1ctl_tx_dm_freq_req_h1(ms, cd->maio, cd->hsn, ma, ma_len, cd->tsc, fn); else l1ctl_tx_dm_freq_req_h0(ms, cd->arfcn, cd->tsc, fn); if (rr->cipher_on) 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, cd->tch_flags); return 0; } /* render list of hopping channels from channel description elements */ static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t *ma_len) { struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = cs->si; struct gsm_settings *set = &ms->settings; int i, index; uint16_t arfcn, pcs; pcs = gsm_refer_pcs(cs->arfcn, s) ? ARFCN_PCS : 0; /* no hopping (no MA), channel description is valid */ if (!cd->h) { *ma_len = 0; return 0; } /* decode mobile allocation */ if (cd->mob_alloc_lv[0]) { struct gsm_sysinfo_freq *freq = s->freq; LOGP(DRR, LOGL_INFO, "decoding mobile allocation\n"); if (cd->cell_desc_lv[0]) { 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 length\n"); return GSM48_RR_CAUSE_ABNORMAL_UNSPEC; } gsm48_decode_freq_list(freq, cd->cell_desc_lv + 1, 16, 0xce, FREQ_TYPE_SERV); } gsm48_decode_mobile_alloc(freq, cd->mob_alloc_lv + 1, cd->mob_alloc_lv[0], ma, ma_len, 0); if (*ma_len < 1) { LOGP(DRR, LOGL_NOTICE, "mobile allocation with no " "frequency available\n"); return GSM48_RR_CAUSE_NO_CELL_ALLOC_A; } } else /* decode frequency list */ if (cd->freq_list_lv[0]) { struct gsm_sysinfo_freq f[1024]; int j = 0; LOGP(DRR, LOGL_INFO, "decoding frequency list\n"); /* get bitmap */ if (gsm48_decode_freq_list(f, cd->freq_list_lv + 1, cd->freq_list_lv[0], 0xce, FREQ_TYPE_SERV)) { LOGP(DRR, LOGL_NOTICE, "frequency list invalid\n"); return GSM48_RR_CAUSE_ABNORMAL_UNSPEC; } /* collect channels from bitmap (1..1023,0) */ for (i = 1; i <= 1024; i++) { if ((f[i & 1023].mask & FREQ_TYPE_SERV)) { LOGP(DRR, LOGL_INFO, "Listed ARFCN #%d: %s\n", j, gsm_print_arfcn((i & 1023) | pcs)); if (j == 64) { LOGP(DRR, LOGL_NOTICE, "frequency list " "exceeds 64 entries!\n"); return GSM48_RR_CAUSE_ABNORMAL_UNSPEC; } ma[j++] = i & 1023; } } *ma_len = j; } else /* decode frequency channel sequence */ if (cd->freq_seq_lv[0]) { int j = 0, inc; LOGP(DRR, LOGL_INFO, "decoding frequency channel sequence\n"); if (cd->freq_seq_lv[0] != 9) { LOGP(DRR, LOGL_NOTICE, "invalid frequency channel " "sequence\n"); return GSM48_RR_CAUSE_ABNORMAL_UNSPEC; } arfcn = cd->freq_seq_lv[1] & 0x7f; LOGP(DRR, LOGL_INFO, "Listed Sequence ARFCN #%d: %s\n", j, gsm_print_arfcn(arfcn | pcs)); ma[j++] = arfcn; for (i = 0; i < 16; i++) { if ((i & 1)) inc = cd->freq_seq_lv[2 + (i >> 1)] & 0x0f; else inc = cd->freq_seq_lv[2 + (i >> 1)] >> 4; if (inc) { arfcn += inc; LOGP(DRR, LOGL_INFO, "Listed Sequence ARFCN " "#%d: %s\n", j, gsm_print_arfcn(i | pcs)); ma[j++] = arfcn; } else arfcn += 15; } *ma_len = j; } else { LOGP(DRR, LOGL_NOTICE, "hopping, but nothing that tells us " "a sequence\n"); return GSM48_RR_CAUSE_ABNORMAL_UNSPEC; } /* convert to band_arfcn and check for unsported frequency */ for (i = 0; i < *ma_len; i++) { 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)))) { LOGP(DRR, LOGL_NOTICE, "Hopping ARFCN %s not " "supported\n", gsm_print_arfcn(arfcn)); return GSM48_RR_CAUSE_FREQ_NOT_IMPL; } } return 0; } /* activate link and send establish request */ static int gsm48_rr_dl_est(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_pag_rsp *pr; uint16_t ma[64]; uint8_t ma_len; /* 3.3.1.1.3.1 */ stop_rr_t3126(rr); /* check if we have to change channel at starting time (we delay) */ if (rr->cd_now.start) { int32_t now, start, diff; uint32_t start_mili = 0; /* how much time do we have left? */ now = ms->meas.last_fn % 42432; start = rr->cd_now.start_tm.fn % 42432; diff = start - now; if (diff < 0) diff += 42432; LOGP(DRR, LOGL_INFO, " (Tnow %d Tstart %d diff %d)\n", now, start, diff); start_mili = (uint32_t)diff * 19580 / 42432 * 10; if (diff >= 32024 || !start_mili) { LOGP(DRR, LOGL_INFO, " -> Start time already " "elapsed\n"); rr->cd_now.start = 0; } else { LOGP(DRR, LOGL_INFO, " -> Start time is %d ms in the " "future\n", start_mili); } #ifndef TEST_FREQUENCY_MOD /* schedule start of IMM.ASS */ rr->modify_state = GSM48_RR_MOD_IMM_ASS; start_rr_t_starting(rr, start_mili / 1000, (start_mili % 1000) * 1000); /* when timer fires, start time is already elapsed */ rr->cd_now.start = 0; return 0; #endif } /* get hopping sequence, if required */ if (gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len)) return -EINVAL; /* clear all sequence numbers for all possible PDs */ rr->v_sd = 0; /* send DL_EST_REQ */ if (rr->rr_est_msg) { LOGP(DRR, LOGL_INFO, "sending establish message\n"); /* use queued message */ nmsg = rr->rr_est_msg; rr->rr_est_msg = 0; /* set sequence number and increment */ gsm48_apply_v_sd(rr, nmsg); } else { /* create paging response */ nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_PAG_RESP; pr = (struct gsm48_pag_rsp *) msgb_put(nmsg, sizeof(*pr)); /* key sequence */ pr->key_seq = gsm_subscr_get_key_seq(ms, subscr); /* classmark 2 */ pr->cm2_len = sizeof(pr->cm2); gsm48_rr_enc_cm2(ms, &pr->cm2, rr->cd_now.arfcn); /* mobile identity */ 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_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_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, false); LOGP(DRR, LOGL_INFO, "sending paging response with " "IMSI\n"); } else { gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_NONE, false); LOGP(DRR, LOGL_INFO, "sending paging response without " "TMSI/IMSI\n"); } } #ifdef TEST_FREQUENCY_MOD LOGP(DRR, LOGL_INFO, " TESTING: frequency modify IMM.ASS\n"); memcpy(&rr->cd_before, &rr->cd_now, sizeof(rr->cd_before)); rr->cd_before.h = 0; rr->cd_before.arfcn = 0; /* activate channel */ gsm48_rr_activate_channel(ms, &rr->cd_before, ma, ma_len); /* render channel "after time" */ gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len); /* schedule change of channel */ gsm48_rr_channel_after_time(ms, &rr->cd_now, ma, ma_len, rr->cd_now.start_tm.fn); #else /* activate channel */ gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len); #endif /* set T200 of SAPI 0 */ ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_sec = T200_DCCH; ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_usec = 0; /* start establishmnet */ return gsm48_send_rsl(ms, RSL_MT_EST_REQ, nmsg, 0); } /* the link is established */ static int gsm48_rr_estab_cnf(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm_support *sup = &ms->support; struct gsm322_cellsel *cs = &ms->cellsel; uint8_t *mode; struct msgb *nmsg; /* if MM has releases before confirm, we start release */ if (rr->state == GSM48_RR_ST_REL_PEND) { LOGP(DRR, LOGL_INFO, "MM already released RR.\n"); /* release message */ nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; mode = msgb_put(nmsg, 2); mode[0] = RSL_IE_RELEASE_MODE; mode[1] = 0; /* normal release */ /* start release */ return gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0); } /* 3.3.1.1.4 */ new_rr_state(rr, GSM48_RR_ST_DEDICATED); /* early classmark sending */ if (cs->si->ecsm && sup->es_ind) gsm48_rr_tx_cm_change(ms); /* send confirm to upper layer */ nmsg = gsm48_rr_msgb_alloc( (rr->rr_est_req) ? GSM48_RR_EST_CNF : GSM48_RR_EST_IND); if (!nmsg) return -ENOMEM; return gsm48_rr_upmsg(ms, nmsg); } /* the link is released in pending state (by l2) */ static int gsm48_rr_rel_ind(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct msgb *nmsg; struct gsm48_rr_hdr *nrrh; /* 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: case GSM48_RR_MOD_HANDO: /* channel is deactivate there */ return gsm48_rr_rel_cnf(ms, msg); case GSM48_RR_MOD_ASSIGN_RESUME: case GSM48_RR_MOD_HANDO_RESUME: rr->modify_state = GSM48_RR_MOD_NONE; break; } LOGP(DSUM, LOGL_INFO, "Radio link is released\n"); /* send inication to upper layer */ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); if (!nmsg) return -ENOMEM; nrrh = (struct gsm48_rr_hdr *)nmsg->data; nrrh->cause = RR_REL_CAUSE_NORMAL; gsm48_rr_upmsg(ms, nmsg); /* start release timer, so UA will be transmitted */ start_rr_t_rel_wait(rr, 1, 500000); /* pending release */ new_rr_state(rr, GSM48_RR_ST_REL_PEND); /* also release SAPI 3 link, if exists */ gsm48_release_sapi3_link(ms); return 0; } /* 9.1.7 CHANNEL RELEASE is received */ static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_chan_rel *cr = (struct gsm48_chan_rel *)gh->data; int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cr); struct tlv_parsed tp; struct msgb *nmsg; uint8_t *mode; if (payload_len < 0) { LOGP(DRR, LOGL_NOTICE, "Short read of CHANNEL RELEASE message\n"); return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); } 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 release request with cause 0x%02x\n", cr->rr_cause); /* BA range */ if (TLVP_PRESENT(&tp, GSM48_IE_BA_RANGE)) { gsm48_decode_ba_range(TLVP_VAL(&tp, GSM48_IE_BA_RANGE), *(TLVP_VAL(&tp, GSM48_IE_BA_RANGE) - 1), rr->ba_range, &rr->ba_ranges, sizeof(rr->ba_range) / sizeof(rr->ba_range[0])); /* NOTE: the ranges are kept until IDLE state is returned * (see new_rr_state) */ } new_rr_state(rr, GSM48_RR_ST_REL_PEND); /* 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); /* disconnect the main signalling link */ nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; mode = msgb_put(nmsg, 2); mode[0] = RSL_IE_RELEASE_MODE; mode[1] = 0; /* normal release */ gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0); /* release SAPI 3 link, if exits */ gsm48_release_sapi3_link(ms); 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, 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, 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 */ if (ch_type != RSL_CHAN_Bm_ACCHs && ch_type != RSL_CHAN_Lm_ACCHs) return -ENOTSUP; /* 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; } /* 9.1.13 FREQUENCY REDEFINITION is received */ static int gsm48_rr_rx_frq_redef(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = cs->si; struct gsm48_frq_redef *fr = msgb_l3(msg); int mob_al_len = msgb_l3len(msg) - sizeof(*fr); uint8_t ch_type, ch_subch, ch_ts; struct gsm48_rr_cd cd; uint8_t cause; uint8_t *st; uint16_t ma[64]; uint8_t ma_len; memcpy(&cd, &rr->cd_now, sizeof(cd)); if (mob_al_len < 0 /* mobile allocation IE must be included */ || fr->mob_alloc_len + 2 > mob_al_len) { /* short read of IE */ LOGP(DRR, LOGL_NOTICE, "Short read of FREQUENCY REDEFINITION " "message.\n"); return -EINVAL; } if (fr->mob_alloc_len > 8) { LOGP(DRR, LOGL_NOTICE, "Moble allocation in FREQUENCY " "REDEFINITION too large.\n"); return -EINVAL; } /* decode channel description */ LOGP(DRR, LOGL_INFO, "FREQUENCY REDEFINITION:\n"); cd.chan_nr = fr->chan_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); return -EINVAL; } if (fr->chan_desc.h0.h) { cd.h = 1; gsm48_decode_chan_h1(&fr->chan_desc, &cd.tsc, &cd.maio, &cd.hsn); LOGP(DRR, LOGL_INFO, " (MAIO %u HSN %u TS %u SS %u TSC %u)\n", cd.maio, cd.hsn, ch_ts, ch_subch, cd.tsc); } else { cd.h = 0; gsm48_decode_chan_h0(&fr->chan_desc, &cd.tsc, &cd.arfcn); 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); } /* mobile allocation */ memcpy(rr->cd_now.mob_alloc_lv, &fr->mob_alloc_len, fr->mob_alloc_len + 1); /* starting time */ st = fr->mob_alloc + fr->mob_alloc_len; gsm48_decode_start_time(&cd, (struct gsm48_start_time *)(st+1)); /* cell channel description */ if (mob_al_len >= fr->mob_alloc_len + 2 + 17 && fr->mob_alloc[fr->mob_alloc_len + 2] == GSM48_IE_CELL_CH_DESC) { const uint8_t *v = fr->mob_alloc + fr->mob_alloc_len + 2 + 1; LOGP(DRR, LOGL_INFO, " using cell channel description)\n"); cd.cell_desc_lv[0] = 16; memcpy(cd.cell_desc_lv + 1, v, 17); } /* render channel "after time" */ cause = gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len); if (cause) return gsm48_rr_tx_rr_status(ms, cause); /* update to new channel data */ memcpy(&rr->cd_now, &cd, sizeof(rr->cd_now)); /* schedule change of channel */ gsm48_rr_channel_after_time(ms, &rr->cd_now, ma, ma_len, rr->cd_now.start_tm.fn); rr->cd_now.start = 0; return 0; } /* 9.1.6 sending CHANNEL MODE MODIFY ACKNOWLEDGE */ static int gsm48_rr_tx_chan_modify_ack(struct osmocom_ms *ms, struct gsm48_chan_desc *cd, uint8_t mode) { struct msgb *nmsg; struct gsm48_hdr *gh; struct gsm48_chan_mode_modify *cm; LOGP(DRR, LOGL_INFO, "CHAN.MODE.MOD ACKNOWLEDGE\n"); nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); cm = (struct gsm48_chan_mode_modify *) msgb_put(nmsg, sizeof(*cm)); gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF_ACK; /* CD */ memcpy(&cm->chan_desc, cd, sizeof(struct gsm48_chan_desc)); /* mode */ cm->mode = mode; return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg, 0); } /* 9.1.5 CHANNEL MODE MODIFY is received */ static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = cs->si; struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_chan_mode_modify *cm = (struct gsm48_chan_mode_modify *)gh->data; 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; int rc; LOGP(DRR, LOGL_INFO, "CHANNEL MODE MODIFY\n"); if (payload_len < 0) { LOGP(DRR, LOGL_NOTICE, "Short read of CHANNEL MODE MODIFY " "message.\n"); return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); } /* decode channel description */ cd->chan_nr = cm->chan_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); return -EINVAL; } if (cm->chan_desc.h0.h) { cd->h = 1; gsm48_decode_chan_h1(&cm->chan_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 mode %u)\n", cm->chan_desc.chan_nr, cd->maio, cd->hsn, ch_ts, ch_subch, cd->tsc, cm->mode); } else { cd->h = 0; gsm48_decode_chan_h0(&cm->chan_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 mode %u)\n", cm->chan_desc.chan_nr, gsm_print_arfcn(cd->arfcn), ch_ts, ch_subch, cd->tsc, cm->mode); } /** * 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; ack: return gsm48_rr_tx_chan_modify_ack(ms, &cm->chan_desc, cd->mode); } /* 9.1.3 sending ASSIGNMENT COMPLETE */ static int gsm48_rr_tx_ass_cpl(struct osmocom_ms *ms, uint8_t cause) { struct msgb *nmsg; struct gsm48_hdr *gh; struct gsm48_ass_cpl *ac; LOGP(DRR, LOGL_INFO, "ASSIGNMENT COMPLETE (cause #%d)\n", cause); nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); ac = (struct gsm48_ass_cpl *) msgb_put(nmsg, sizeof(*ac)); gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_ASS_COMPL; /* RR_CAUSE */ ac->rr_cause = cause; /* set T200 of SAPI 0 */ ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_sec = T200_DCCH; ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_usec = 0; return gsm48_send_rsl(ms, RSL_MT_RES_REQ, nmsg, 0); } /* 9.1.4 sending ASSIGNMENT FAILURE */ static int gsm48_rr_tx_ass_fail(struct osmocom_ms *ms, uint8_t cause, uint8_t rsl_prim) { struct msgb *nmsg; struct gsm48_hdr *gh; struct gsm48_ass_fail *af; LOGP(DRR, LOGL_INFO, "ASSIGNMENT FAILURE (cause #%d)\n", cause); nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); af = (struct gsm48_ass_fail *) msgb_put(nmsg, sizeof(*af)); gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_ASS_COMPL; /* RR_CAUSE */ af->rr_cause = cause; return gsm48_send_rsl(ms, rsl_prim, nmsg, 0); } /* 9.1.2 ASSIGNMENT COMMAND is received */ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = cs->si; struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_ass_cmd *ac = (struct gsm48_ass_cmd *)gh->data; int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ac); struct tlv_parsed tp; struct gsm48_rr_cd *cda = &rr->cd_after; struct gsm48_rr_cd *cdb = &rr->cd_before; uint8_t ch_type, ch_subch, ch_ts; uint8_t before_time = 0; uint16_t ma[64]; uint8_t ma_len; uint32_t start_mili = 0; uint8_t cause; struct msgb *nmsg; LOGP(DRR, LOGL_INFO, "ASSIGNMENT COMMAND\n"); memset(cda, 0, sizeof(*cda)); cda->ind_tx_power = rr->cd_now.ind_tx_power; memset(cdb, 0, sizeof(*cdb)); 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); } 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); } /* 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; 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, &cdb->hsn); LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x MAIO %u " "HSN %u TS %u SS %u TSC %u)\n", ccd->chan_nr, cdb->maio, cdb->hsn, ch_ts, ch_subch, cdb->tsc); } else { cdb->h = 0; gsm48_decode_chan_h0(ccd, &cdb->tsc, &cdb->arfcn); 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), ch_ts, ch_subch, cdb->tsc); } before_time = 1; } /* decode channel description (after time) */ cda->chan_nr = ac->chan_desc.chan_nr; 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, &cda->hsn); LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x MAIO %u HSN %u " "TS %u SS %u TSC %u)\n", ac->chan_desc.chan_nr, cda->maio, cda->hsn, ch_ts, ch_subch, cda->tsc); } else { cda->h = 0; gsm48_decode_chan_h0(&ac->chan_desc, &cda->tsc, &cda->arfcn); 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); } /* starting time */ #ifdef TEST_STARTING_TIMER cda->start = 1; cda->start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432; LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n"); #else if (TLVP_PRESENT(&tp, GSM48_IE_START_TIME)) { gsm48_decode_start_time(cda, (struct gsm48_start_time *) TLVP_VAL(&tp, GSM48_IE_START_TIME)); /* 9.1.2.5 "... before time IE is not present..." */ if (!before_time) { LOGP(DRR, LOGL_INFO, " -> channel description after " "time only, but starting time\n"); } else LOGP(DRR, LOGL_INFO, " -> channel description before " "and after time\n"); } else { /* 9.1.2.5 "... IEs unnecessary in this message." */ if (before_time) { before_time = 0; LOGP(DRR, LOGL_INFO, " -> channel description before " "time, but no starting time, ignoring!\n"); } } #endif /* mobile allocation / frequency list after time */ if (cda->h) { if (TLVP_PRESENT(&tp, GSM48_IE_MA_AFTER)) { const uint8_t *lv = TLVP_VAL(&tp, GSM48_IE_MA_AFTER) - 1; LOGP(DRR, LOGL_INFO, " after: hopping required and " "mobile allocation available\n"); if (*lv + 1 > sizeof(cda->mob_alloc_lv)) { LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); return -ENOMEM; } memcpy(cda->mob_alloc_lv, lv, *lv + 1); } else if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_AFTER)) { const uint8_t *lv = TLVP_VAL(&tp, GSM48_IE_FREQ_L_AFTER) - 1; LOGP(DRR, LOGL_INFO, " after: hopping required and " "frequency list available\n"); if (*lv + 1 > sizeof(cda->freq_list_lv)) { LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); return -ENOMEM; } memcpy(cda->freq_list_lv, lv, *lv + 1); } else { LOGP(DRR, LOGL_NOTICE, " after: hopping required, but " "no mobile allocation / frequency list\n"); } } /* mobile allocation / frequency list before time */ if (cdb->h) { if (TLVP_PRESENT(&tp, GSM48_IE_MA_BEFORE)) { const uint8_t *lv = TLVP_VAL(&tp, GSM48_IE_MA_BEFORE) - 1; LOGP(DRR, LOGL_INFO, " before: hopping required and " "mobile allocation available\n"); if (*lv + 1 > sizeof(cdb->mob_alloc_lv)) { LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); return -ENOMEM; } memcpy(cdb->mob_alloc_lv, lv, *lv + 1); } else if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_BEFORE)) { const uint8_t *lv = TLVP_VAL(&tp, GSM48_IE_FREQ_L_BEFORE) - 1; LOGP(DRR, LOGL_INFO, " before: hopping required and " "frequency list available\n"); if (*lv + 1 > sizeof(cdb->freq_list_lv)) { LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); return -ENOMEM; } memcpy(cdb->freq_list_lv, lv, *lv + 1); } else if (TLVP_PRESENT(&tp, GSM48_IE_F_CH_SEQ_BEFORE)) { const uint8_t *v = TLVP_VAL(&tp, GSM48_IE_F_CH_SEQ_BEFORE); uint8_t len = TLVP_LEN(&tp, GSM48_IE_F_CH_SEQ_BEFORE); LOGP(DRR, LOGL_INFO, " before: hopping required and " "frequency channel sequence available\n"); if (len + 1 > sizeof(cdb->freq_seq_lv)) { LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); return -ENOMEM; } cdb->freq_seq_lv[0] = len; memcpy(cdb->freq_seq_lv + 1, v, len); } else if (cda->mob_alloc_lv[0]) { LOGP(DRR, LOGL_INFO, " before: hopping required and " "mobile allocation not available, using " "mobile allocation after time\n"); memcpy(cdb->mob_alloc_lv, cda->mob_alloc_lv, sizeof(cdb->mob_alloc_lv)); } else if (cda->freq_list_lv[0]) { LOGP(DRR, LOGL_INFO, " before: hopping required and " "frequency list not available, using " "frequency list after time\n"); memcpy(cdb->freq_list_lv, cda->freq_list_lv, sizeof(cdb->freq_list_lv)); } else { LOGP(DRR, LOGL_NOTICE, " before: hopping required, but " "no mobile allocation / frequency list\n"); } } /* cell channel description */ if (TLVP_PRESENT(&tp, GSM48_IE_CELL_CH_DESC)) { const uint8_t *v = TLVP_VAL(&tp, GSM48_IE_CELL_CH_DESC); uint8_t len = TLVP_LEN(&tp, GSM48_IE_CELL_CH_DESC); LOGP(DRR, LOGL_INFO, " both: using cell channel description " "in case of mobile allocation\n"); if (len + 1 > sizeof(cdb->cell_desc_lv)) { LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); return -ENOMEM; } cdb->cell_desc_lv[0] = len; memcpy(cdb->cell_desc_lv + 1, v, len); cda->cell_desc_lv[0] = len; memcpy(cda->cell_desc_lv + 1, v, len); } else { /* keep old */ memcpy(cdb->cell_desc_lv, rr->cd_now.cell_desc_lv, sizeof(cdb->cell_desc_lv)); memcpy(cda->cell_desc_lv, rr->cd_now.cell_desc_lv, sizeof(cda->cell_desc_lv)); } /* channel mode */ if (TLVP_PRESENT(&tp, GSM48_IE_CHANMODE_1)) { cda->mode = cdb->mode = *TLVP_VAL(&tp, GSM48_IE_CHANMODE_1); LOGP(DRR, LOGL_INFO, " both: changing channel mode 0x%02x\n", cda->mode); } else cda->mode = cdb->mode = rr->cd_now.mode; /* cipher mode setting */ if (TLVP_PRESENT(&tp, GSM48_IE_CIP_MODE_SET)) { cda->cipher = cdb->cipher = *TLVP_VAL(&tp, GSM48_IE_CIP_MODE_SET); LOGP(DRR, LOGL_INFO, " both: changing cipher mode 0x%02x\n", cda->cipher); } else cda->cipher = cdb->cipher = rr->cd_now.cipher; /* power command and TA (before and after time) */ gsm48_decode_power_cmd_acc( (struct gsm48_power_cmd *) &ac->power_command, &cda->ind_tx_power, NULL); cdb->ind_tx_power = cda->ind_tx_power; cda->ind_ta = cdb->ind_ta = rr->cd_now.ind_ta; /* same cell */ LOGP(DRR, LOGL_INFO, " both: (tx_power %d TA %d)\n", cda->ind_tx_power, cda->ind_ta); /* check if we have to change channel at starting time */ if (cda->start) { int32_t now, start, diff; /* how much time do we have left? */ now = ms->meas.last_fn % 42432; start = cda->start_tm.fn % 42432; diff = start - now; if (diff < 0) diff += 42432; LOGP(DRR, LOGL_INFO, " after: (Tnow %d Tstart %d diff %d)\n", now, start, diff); start_mili = (uint32_t)diff * 19580 / 42432 * 10; if (diff >= 32024 || !start_mili) { LOGP(DRR, LOGL_INFO, " -> Start time already " "elapsed\n"); before_time = 0; cda->start = 0; } else { LOGP(DRR, LOGL_INFO, " -> Start time is %d ms in the " "future\n", start_mili); } } /* check if channels are valid */ cause = gsm48_rr_check_mode(ms, cda->chan_nr, cda->mode); if (cause) return gsm48_rr_tx_ass_fail(ms, cause, RSL_MT_DATA_REQ); if (before_time) { cause = gsm48_rr_render_ma(ms, cdb, ma, &ma_len); if (cause) return gsm48_rr_tx_ass_fail(ms, cause, RSL_MT_DATA_REQ); } cause = gsm48_rr_render_ma(ms, cda, ma, &ma_len); if (cause) return gsm48_rr_tx_ass_fail(ms, cause, RSL_MT_DATA_REQ); #ifdef TEST_FREQUENCY_MOD LOGP(DRR, LOGL_INFO, " TESTING: frequency modify ASS.CMD\n"); before_time = 1; memcpy(cdb, cda, sizeof(*cdb)); cdb->h = 0; cdb->arfcn = 0; #endif /* schedule start of assignment */ rr->modify_state = GSM48_RR_MOD_ASSIGN; if (!before_time && cda->start) { start_rr_t_starting(rr, start_mili / 1000, start_mili % 1000); /* when timer fires, start time is already elapsed */ cda->start = 0; return 0; } /* if no starting time, start suspension of current link directly */ LOGP(DRR, LOGL_INFO, "request suspension of data link\n"); nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; gsm48_send_rsl(ms, RSL_MT_SUSP_REQ, nmsg, 0); /* release SAPI 3 link, if exits * FIXME: suspend and resume afterward */ gsm48_release_sapi3_link(ms); return 0; } /* 9.1.16 sending HANDOVER COMPLETE */ static int gsm48_rr_tx_hando_cpl(struct osmocom_ms *ms, uint8_t cause) { struct msgb *nmsg; struct gsm48_hdr *gh; struct gsm48_ho_cpl *hc; LOGP(DRR, LOGL_INFO, "HANDOVER COMPLETE (cause #%d)\n", cause); nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); hc = (struct gsm48_ho_cpl *) msgb_put(nmsg, sizeof(*hc)); gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_HANDO_COMPL; /* RR_CAUSE */ hc->rr_cause = cause; // FIXME: mobile observed time /* set T200 of SAPI 0 */ ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_sec = T200_DCCH; ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_usec = 0; return gsm48_send_rsl(ms, RSL_MT_RES_REQ, nmsg, 0); } /* 9.1.4 sending HANDOVER FAILURE */ static int gsm48_rr_tx_hando_fail(struct osmocom_ms *ms, uint8_t cause, uint8_t rsl_prim) { struct msgb *nmsg; struct gsm48_hdr *gh; struct gsm48_ho_fail *hf; LOGP(DRR, LOGL_INFO, "HANDOVER FAILURE (cause #%d)\n", cause); nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); hf = (struct gsm48_ho_fail *) msgb_put(nmsg, sizeof(*hf)); gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_ASS_COMPL; /* RR_CAUSE */ hf->rr_cause = cause; return gsm48_send_rsl(ms, rsl_prim, nmsg, 0); } /* receiving HANDOVER COMMAND message (9.1.15) */ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = cs->si; struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_ho_cmd *ho = (struct gsm48_ho_cmd *)gh->data; int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ho); struct tlv_parsed tp; struct gsm48_rr_cd *cda = &rr->cd_after; struct gsm48_rr_cd *cdb = &rr->cd_before; uint16_t arfcn; uint8_t bcc, ncc; uint8_t ch_type, ch_subch, ch_ts; uint8_t before_time = 0; uint16_t ma[64]; uint8_t ma_len; uint32_t start_mili = 0; uint8_t cause; struct msgb *nmsg; LOGP(DRR, LOGL_INFO, "HANDOVER COMMAND\n"); memset(cda, 0, sizeof(*cda)); cda->ind_tx_power = rr->cd_now.ind_tx_power; memset(cdb, 0, sizeof(*cdb)); 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); } 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 */ gsm48_decode_cell_desc(&ho->cell_desc, &arfcn, &ncc, &bcc); /* handover reference */ rr->chan_req_val = ho->ho_ref; rr->chan_req_mask = 0x00; /* sync ind */ if (TLVP_PRESENT(&tp, GSM48_IE_SYNC_IND)) { gsm48_decode_sync_ind(rr, (struct gsm48_sync_ind *) TLVP_VAL(&tp, GSM48_IE_SYNC_IND)); LOGP(DRR, LOGL_INFO, " (sync_ind=%d rot=%d nci=%d)\n", rr->hando_sync_ind, rr->hando_rot, rr->hando_nci); } /* 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; 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, &cdb->hsn); LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x MAIO %u " "HSN %u TS %u SS %u TSC %u)\n", ccd->chan_nr, cdb->maio, cdb->hsn, ch_ts, ch_subch, cdb->tsc); } else { cdb->h = 0; gsm48_decode_chan_h0(ccd, &cdb->tsc, &cdb->arfcn); 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), ch_ts, ch_subch, cdb->tsc); } before_time = 1; } /* decode channel description (after time) */ cda->chan_nr = ho->chan_desc.chan_nr; 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, &cda->hsn); LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x MAIO %u HSN %u " "TS %u SS %u TSC %u)\n", ho->chan_desc.chan_nr, cda->maio, cda->hsn, ch_ts, ch_subch, cda->tsc); } else { cda->h = 0; gsm48_decode_chan_h0(&ho->chan_desc, &cda->tsc, &cda->arfcn); 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); } /* starting time */ #ifdef TEST_STARTING_TIMER cda->start = 1; cda->start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432; LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n"); #else if (TLVP_PRESENT(&tp, GSM48_IE_START_TIME)) { gsm48_decode_start_time(cda, (struct gsm48_start_time *) TLVP_VAL(&tp, GSM48_IE_START_TIME)); /* 9.1.2.5 "... before time IE is not present..." */ if (!before_time) { LOGP(DRR, LOGL_INFO, " -> channel description after " "time only, but starting time\n"); } else LOGP(DRR, LOGL_INFO, " -> channel description before " "and after time\n"); } else { /* 9.1.2.5 "... IEs unnecessary in this message." */ if (before_time) { before_time = 0; LOGP(DRR, LOGL_INFO, " -> channel description before " "time, but no starting time, ignoring!\n"); } } #endif /* mobile allocation / frequency list after time */ if (cda->h) { if (TLVP_PRESENT(&tp, GSM48_IE_MA_AFTER)) { const uint8_t *lv = TLVP_VAL(&tp, GSM48_IE_MA_AFTER) - 1; LOGP(DRR, LOGL_INFO, " after: hopping required and " "mobile allocation available\n"); if (*lv + 1 > sizeof(cda->mob_alloc_lv)) { LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); return -ENOMEM; } memcpy(cda->mob_alloc_lv, lv, *lv + 1); } else if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_AFTER)) { const uint8_t *lv = TLVP_VAL(&tp, GSM48_IE_FREQ_L_AFTER) - 1; LOGP(DRR, LOGL_INFO, " after: hopping required and " "frequency list available\n"); if (*lv + 1 > sizeof(cda->freq_list_lv)) { LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); return -ENOMEM; } memcpy(cda->freq_list_lv, lv, *lv + 1); } else { LOGP(DRR, LOGL_NOTICE, " after: hopping required, but " "no mobile allocation / frequency list\n"); } } /* mobile allocation / frequency list before time */ if (cdb->h) { if (TLVP_PRESENT(&tp, GSM48_IE_MA_BEFORE)) { const uint8_t *lv = TLVP_VAL(&tp, GSM48_IE_MA_BEFORE) - 1; LOGP(DRR, LOGL_INFO, " before: hopping required and " "mobile allocation available\n"); if (*lv + 1 > sizeof(cdb->mob_alloc_lv)) { LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); return -ENOMEM; } memcpy(cdb->mob_alloc_lv, lv, *lv + 1); } else if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_BEFORE)) { const uint8_t *lv = TLVP_VAL(&tp, GSM48_IE_FREQ_L_BEFORE) - 1; LOGP(DRR, LOGL_INFO, " before: hopping required and " "frequency list available\n"); if (*lv + 1 > sizeof(cdb->freq_list_lv)) { LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); return -ENOMEM; } memcpy(cdb->freq_list_lv, lv, *lv + 1); } else if (TLVP_PRESENT(&tp, GSM48_IE_F_CH_SEQ_BEFORE)) { const uint8_t *v = TLVP_VAL(&tp, GSM48_IE_F_CH_SEQ_BEFORE); uint8_t len = TLVP_LEN(&tp, GSM48_IE_F_CH_SEQ_BEFORE); LOGP(DRR, LOGL_INFO, " before: hopping required and " "frequency channel sequence available\n"); if (len + 1 > sizeof(cdb->freq_seq_lv)) { LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); return -ENOMEM; } cdb->freq_seq_lv[0] = len; memcpy(cdb->freq_seq_lv, v + 1, *v); } else if (cda->mob_alloc_lv[0]) { LOGP(DRR, LOGL_INFO, " before: hopping required and " "mobile allocation not available, using " "mobile allocation after time\n"); memcpy(cdb->mob_alloc_lv, cda->mob_alloc_lv, sizeof(cdb->mob_alloc_lv)); } else if (cda->freq_list_lv[0]) { LOGP(DRR, LOGL_INFO, " before: hopping required and " "frequency list not available, using " "frequency list after time\n"); memcpy(cdb->freq_list_lv, cda->freq_list_lv, sizeof(cdb->freq_list_lv)); } else { LOGP(DRR, LOGL_NOTICE, " before: hopping required, but " "no mobile allocation / frequency list\n"); } } /* cell channel description */ if (TLVP_PRESENT(&tp, GSM48_IE_CELL_CH_DESC)) { const uint8_t *v = TLVP_VAL(&tp, GSM48_IE_CELL_CH_DESC); uint8_t len = TLVP_LEN(&tp, GSM48_IE_CELL_CH_DESC); LOGP(DRR, LOGL_INFO, " both: using cell channel description " "in case of mobile allocation\n"); if (len + 1 > sizeof(cdb->cell_desc_lv)) { LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); return -ENOMEM; } cdb->cell_desc_lv[0] = len; memcpy(cdb->cell_desc_lv + 1, v, len); cda->cell_desc_lv[0] = len; memcpy(cda->cell_desc_lv + 1, v, len); } else { /* keep old */ memcpy(cdb->cell_desc_lv, rr->cd_now.cell_desc_lv, sizeof(cdb->cell_desc_lv)); memcpy(cda->cell_desc_lv, rr->cd_now.cell_desc_lv, sizeof(cda->cell_desc_lv)); } /* channel mode */ if (TLVP_PRESENT(&tp, GSM48_IE_CHANMODE_1)) { cda->mode = cdb->mode = *TLVP_VAL(&tp, GSM48_IE_CHANMODE_1); LOGP(DRR, LOGL_INFO, " both: changing channel mode 0x%02x\n", cda->mode); } else cda->mode = cdb->mode = rr->cd_now.mode; /* cipher mode setting */ if (TLVP_PRESENT(&tp, GSM48_IE_CIP_MODE_SET)) { cda->cipher = cdb->cipher = *TLVP_VAL(&tp, GSM48_IE_CIP_MODE_SET); LOGP(DRR, LOGL_INFO, " both: changing cipher mode 0x%02x\n", cda->cipher); } else cda->cipher = cdb->cipher = rr->cd_now.cipher; /* power command and TA (before and after time) */ gsm48_decode_power_cmd_acc( (struct gsm48_power_cmd *) &ho->power_command, &cda->ind_tx_power, &rr->hando_act); cdb->ind_tx_power = cda->ind_tx_power; cda->ind_ta = cdb->ind_ta = rr->cd_now.ind_ta; /* same cell */ LOGP(DRR, LOGL_INFO, " both: (tx_power %d TA %d access=%s)\n", cda->ind_tx_power, cda->ind_ta, (rr->hando_act) ? "optional" : "mandatory"); /* check if we have to change channel at starting time */ if (cda->start) { int32_t now, start, diff; /* how much time do we have left? */ now = ms->meas.last_fn % 42432; start = cda->start_tm.fn % 42432; diff = start - now; if (diff < 0) diff += 42432; LOGP(DRR, LOGL_INFO, " after: (Tnow %d Tstart %d diff %d)\n", now, start, diff); start_mili = (uint32_t)diff * 19580 / 42432 * 10; if (diff >= 32024 || !start_mili) { LOGP(DRR, LOGL_INFO, " -> Start time already " "elapsed\n"); before_time = 0; cda->start = 0; } else { LOGP(DRR, LOGL_INFO, " -> Start time is %d ms in the " "future\n", start_mili); } } /* check if channels are valid */ if (before_time) { cause = gsm48_rr_render_ma(ms, cdb, ma, &ma_len); if (cause) return gsm48_rr_tx_hando_fail(ms, cause, RSL_MT_DATA_REQ); } cause = gsm48_rr_render_ma(ms, cda, ma, &ma_len); if (cause) return gsm48_rr_tx_hando_fail(ms, cause, RSL_MT_DATA_REQ); #if 0 if (not supported) { LOGP(DRR, LOGL_NOTICE, "New channel is not supported.\n"); return GSM48_RR_CAUSE_CHAN_MODE_UNACCEPT; } #endif #ifdef TEST_FREQUENCY_MOD LOGP(DRR, LOGL_INFO, " TESTING: frequency modify HANDO.CMD\n"); before_time = 1; memcpy(cdb, cda, sizeof(*cdb)); cdb->h = 0; cdb->arfcn = 0; #endif /* schedule start of handover */ rr->modify_state = GSM48_RR_MOD_HANDO; if (!before_time && cda->start) { start_rr_t_starting(rr, start_mili / 1000, start_mili % 1000); /* when timer fires, start time is already elapsed */ cda->start = 0; return 0; } /* if no starting time, start suspension of current link directly */ LOGP(DRR, LOGL_INFO, "request suspension of data link\n"); nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; gsm48_send_rsl(ms, RSL_MT_SUSP_REQ, nmsg, 0); /* release SAPI 3 link, if exits * FIXME: suspend and resume afterward */ gsm48_release_sapi3_link(ms); 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) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct msgb *msg; while((msg = msgb_dequeue(&rr->downqueue))) { struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *) msg->data; uint8_t sapi = rrh->sapi; LOGP(DRR, LOGL_INFO, "Sending queued message.\n"); if (sapi && rr->sapi3_state != GSM48_RR_SAPI3ST_ESTAB) { LOGP(DRR, LOGL_INFO, "Dropping SAPI 3 msg, no link!\n"); msgb_free(msg); return 0; } gsm48_send_rsl(ms, RSL_MT_DATA_REQ, msg, 0); } return 0; } /* 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; if (rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) { LOGP(DRR, LOGL_INFO, "data link established on uplink\n"); /* 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"); /* transmit queued frames during ho / ass transition */ gsm48_rr_dequeue_down(ms); rr->modify_state = GSM48_RR_MOD_NONE; } return 0; } /* suspend confirm in dedicated mode */ static int gsm48_rr_susp_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; if (rr->modify_state) { uint16_t ma[64]; uint8_t ma_len; /* deactivating dedicated mode */ LOGP(DRR, LOGL_INFO, "suspension coplete, leaving dedicated " "mode\n"); l1ctl_tx_dm_rel_req(ms); ms->meas.rl_fail = 0; rr->dm_est = 0; l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED); /* store current channel descriptions */ memcpy(&rr->cd_last, &rr->cd_now, sizeof(rr->cd_last)); /* copy channel description "after time" */ memcpy(&rr->cd_now, &rr->cd_after, sizeof(rr->cd_now)); if (rr->cd_after.start) { /* render channel "before time" */ gsm48_rr_render_ma(ms, &rr->cd_before, ma, &ma_len); /* activate channel */ gsm48_rr_activate_channel(ms, &rr->cd_before, ma, ma_len); /* render channel "after time" */ gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len); /* schedule change of channel */ gsm48_rr_channel_after_time(ms, &rr->cd_now, ma, ma_len, rr->cd_now.start_tm.fn); } else { /* render channel "after time" */ gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len); /* activate channel */ gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len); } /* send DL-RESUME REQUEST */ LOGP(DRR, LOGL_INFO, "request resume of data link\n"); switch (rr->modify_state) { case GSM48_RR_MOD_ASSIGN: gsm48_rr_tx_ass_cpl(ms, GSM48_RR_CAUSE_NORMAL); break; case GSM48_RR_MOD_HANDO: gsm48_rr_tx_hando_cpl(ms, GSM48_RR_CAUSE_NORMAL); break; } #ifdef TODO /* trigger RACH */ if (rr->modify_state == GSM48_RR_MOD_HANDO) { gsm48_rr_tx_hando_access(ms); rr->hando_acc_left = 3; } #endif } return 0; } /* * radio ressource requests */ /* establish request for dedicated mode */ static int gsm48_rr_est_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 gsm_subscriber *subscr = &ms->subscr; struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *) msg->data; struct gsm48_hdr *gh = msgb_l3(msg); uint8_t cause; struct msgb *nmsg; 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) { LOGP(DRR, LOGL_INFO, "T3122 running, rejecting!\n"); cause = RR_REL_CAUSE_T3122; reject: LOGP(DSUM, LOGL_INFO, "Establishing radio link not " "possible\n"); nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); if (!nmsg) return -ENOMEM; nrrh = (struct gsm48_rr_hdr *)nmsg->data; nrrh->cause = cause; return gsm48_rr_upmsg(ms, nmsg); } LOGP(DRR, LOGL_INFO, "T3122 running, but emergency call\n"); stop_rr_t3122(rr); } /* if state is not idle */ if (rr->state != GSM48_RR_ST_IDLE) { LOGP(DRR, LOGL_INFO, "We are not IDLE yet, rejecting!\n"); cause = RR_REL_CAUSE_TRY_LATER; goto reject; } /* cell selected */ if (!cs->selected) { LOGP(DRR, LOGL_INFO, "No cell selected, rejecting!\n"); cause = RR_REL_CAUSE_TRY_LATER; goto reject; } /* check if camping */ if (cs->state != GSM322_C3_CAMPED_NORMALLY && rrh->cause != RR_EST_CAUSE_EMERGENCY) { LOGP(DRR, LOGL_INFO, "Not camping normally, rejecting! " "(cs->state = %d)\n", cs->state); cause = RR_REL_CAUSE_EMERGENCY_ONLY; goto reject; } 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; } /* check for relevant information */ if (!s->si3) { LOGP(DRR, LOGL_INFO, "Not enough SI, rejecting!\n"); cause = RR_REL_CAUSE_TRY_LATER; goto reject; } /* 3.3.1.1.1 */ if (!subscr->acc_barr && s->cell_barr) { LOGP(DRR, LOGL_INFO, "Cell barred, rejecting!\n"); cause = RR_REL_CAUSE_NOT_AUTHORIZED; goto reject; } if (rrh->cause == RR_EST_CAUSE_EMERGENCY) acc_class = subscr->acc_class | 0x0400; else acc_class = subscr->acc_class & 0xfbff; if (!subscr->acc_barr && !(acc_class & (s->class_barr ^ 0xffff))) { LOGP(DRR, LOGL_INFO, "Cell barred for our access class (access " "%04x barred %04x)!\n", acc_class, s->class_barr); cause = RR_REL_CAUSE_NOT_AUTHORIZED; goto reject; } /* requested by RR */ rr->rr_est_req = 1; /* clone and store REQUEST message */ if (!gh) { LOGP(DRR, LOGL_ERROR, "Error, missing l3 message\n"); return -EINVAL; } rr->rr_est_msg = gsm48_l3_msgb_alloc(); if (!rr->rr_est_msg) return -ENOMEM; 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) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *) msg->data; uint8_t sapi = rrh->sapi; if (rr->state != GSM48_RR_ST_DEDICATED) { msgb_free(msg); return -EINVAL; } /* pull RR header */ msgb_pull(msg, sizeof(struct gsm48_rr_hdr)); /* set sequence number and increment */ gsm48_apply_v_sd(rr, msg); /* queue message, during handover or assignment procedure */ if (rr->modify_state == GSM48_RR_MOD_ASSIGN || rr->modify_state == GSM48_RR_MOD_HANDO) { LOGP(DRR, LOGL_INFO, "Queueing message during suspend.\n"); msgb_enqueue(&rr->downqueue, msg); return 0; } if (sapi && rr->sapi3_state != GSM48_RR_SAPI3ST_ESTAB) { LOGP(DRR, LOGL_INFO, "Dropping SAPI 3 msg, no link!\n"); msgb_free(msg); return 0; } /* forward message */ return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, msg, sapi ? rr->sapi3_link_id : 0); } /* * data indications from data link */ /* 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) { uint8_t skip_ind = (gh->proto_discr & 0xf0) >> 4; /* ignore if skip indicator is not B'0000' */ if (skip_ind) { msgb_free(msg); return 0; } switch(gh->msg_type) { case GSM48_MT_RR_ADD_ASS: rc = gsm48_rr_rx_add_ass(ms, msg); break; case GSM48_MT_RR_ASS_CMD: rc = gsm48_rr_rx_ass_cmd(ms, msg); break; case GSM48_MT_RR_CIPH_M_CMD: rc = gsm48_rr_rx_cip_mode_cmd(ms, msg); break; case GSM48_MT_RR_CLSM_ENQ: rc = gsm48_rr_rx_cm_enq(ms, msg); break; case GSM48_MT_RR_CHAN_MODE_MODIF: rc = gsm48_rr_rx_chan_modify(ms, msg); break; case GSM48_MT_RR_HANDO_CMD: rc = gsm48_rr_rx_hando_cmd(ms, msg); break; case GSM48_MT_RR_FREQ_REDEF: rc = gsm48_rr_rx_frq_redef(ms, msg); break; 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; default: LOGP(DRR, LOGL_NOTICE, "Message type 0x%02x unknown.\n", gh->msg_type); /* status message */ gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_MSG_TYPE_N); } 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 */ msgb_pull(msg, (long)msgb_l3(msg) - (long)msg->data); /* push RR header */ 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); } /* receive BCCH at RR layer */ static int gsm48_rr_rx_bcch(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_system_information_type_header *sih = msgb_l3(msg); switch (sih->system_information) { case GSM48_MT_RR_SYSINFO_1: return gsm48_rr_rx_sysinfo1(ms, msg); case GSM48_MT_RR_SYSINFO_2: return gsm48_rr_rx_sysinfo2(ms, msg); case GSM48_MT_RR_SYSINFO_2bis: return gsm48_rr_rx_sysinfo2bis(ms, msg); case GSM48_MT_RR_SYSINFO_2ter: return gsm48_rr_rx_sysinfo2ter(ms, msg); case GSM48_MT_RR_SYSINFO_3: 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", sih->system_information); #endif return -EINVAL; } } /* receive CCCH at RR layer */ 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); case GSM48_MT_RR_PAG_REQ_2: return gsm48_rr_rx_pag_req_2(ms, msg); case GSM48_MT_RR_PAG_REQ_3: return gsm48_rr_rx_pag_req_3(ms, msg); case GSM48_MT_RR_IMM_ASS: return gsm48_rr_rx_imm_ass(ms, msg); case GSM48_MT_RR_IMM_ASS_EXT: 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", sih->system_information); #endif return -EINVAL; } } #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) { 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; } } /* 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; } } /* 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", gh->msg_type); return -EINVAL; } } /* 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); 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; } msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO); if (cs->ccch_state != GSM322_CCCH_ST_SYNC && cs->ccch_state != GSM322_CCCH_ST_DATA) return -EINVAL; /* temporary moved here until confirm is fixed */ if (cs->ccch_state != GSM322_CCCH_ST_DATA) { 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); /* set timer for reading BCCH */ if (cs->state == GSM322_C2_STORED_CELL_SEL || cs->state == GSM322_C1_NORMAL_CELL_SEL || cs->state == GSM322_C6_ANY_CELL_SEL || cs->state == GSM322_C4_NORMAL_CELL_RESEL || cs->state == GSM322_C8_ANY_CELL_RESEL || cs->state == GSM322_C5_CHOOSE_CELL || cs->state == GSM322_C9_CHOOSE_ANY_CELL || cs->state == GSM322_PLMN_SEARCH || cs->state == GSM322_HPLMN_SEARCH) start_cs_timer(cs, ms->support.scan_to, 0); // TODO: timer depends on BCCH config } 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); case RSL_CHAN_BCCH: return gsm48_rr_rx_bcch(ms, msg); case RSL_CHAN_Bm_ACCHs: 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", rllh->chan_nr); return -EINVAL; } } /* 3.4.13.3 RR abort in dedicated mode (also in conn. pending mode) */ static int gsm48_rr_abort_req(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; uint8_t *mode; /* stop pending RACH timer */ stop_rr_t3126(rr); /* release "normally" if we are in dedicated mode */ if (rr->state == GSM48_RR_ST_DEDICATED) { struct msgb *nmsg; LOGP(DRR, LOGL_INFO, "Abort in dedicated state, send release " "to layer 2.\n"); new_rr_state(rr, GSM48_RR_ST_REL_PEND); /* release message */ nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; mode = msgb_put(nmsg, 2); mode[0] = RSL_IE_RELEASE_MODE; mode[1] = 0; /* normal release */ gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0); /* release SAPI 3 link, if exits */ gsm48_release_sapi3_link(ms); return 0; } LOGP(DRR, LOGL_INFO, "Abort in connection pending state, return to " "idle state.\n"); /* return idle */ new_rr_state(rr, GSM48_RR_ST_IDLE); return 0; } /* release confirm */ static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct msgb *nmsg; struct gsm48_rr_hdr *nrrh; uint8_t cause = RR_REL_CAUSE_NORMAL; 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: case GSM48_RR_MOD_HANDO: /* deactivate channel */ l1ctl_tx_dm_rel_req(ms); ms->meas.rl_fail = 0; rr->dm_est = 0; l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED); /* get old channel description */ memcpy(&rr->cd_now, &rr->cd_last, sizeof(rr->cd_now)); /* render and change radio to old channel */ gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len); gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len); /* re-establish old link */ nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; if (rr->modify_state == GSM48_RR_MOD_ASSIGN) { rr->modify_state = GSM48_RR_MOD_ASSIGN_RESUME; return gsm48_rr_tx_ass_fail(ms, GSM48_RR_CAUSE_ABNORMAL_UNSPEC, RSL_MT_RECON_REQ); } else { rr->modify_state = GSM48_RR_MOD_HANDO_RESUME; return gsm48_rr_tx_hando_fail(ms, GSM48_RR_CAUSE_ABNORMAL_UNSPEC, RSL_MT_RECON_REQ); } /* returns above */ case GSM48_RR_MOD_ASSIGN_RESUME: case GSM48_RR_MOD_HANDO_RESUME: rr->modify_state = GSM48_RR_MOD_NONE; cause = RR_REL_CAUSE_LINK_FAILURE; break; } LOGP(DSUM, LOGL_INFO, "Requested channel aborted\n"); /* stop T3211 if running */ stop_rr_t3110(rr); /* send release indication */ 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; nrrh->cause = cause; gsm48_rr_upmsg(ms, nmsg); /* return idle */ new_rr_state(rr, GSM48_RR_ST_IDLE); return 0; } /* MDL-ERROR */ static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); struct msgb *nmsg; struct gsm48_rr_hdr *nrrh; uint8_t *mode; uint8_t cause = rllh->data[2]; uint8_t link_id = rllh->link_id; switch (cause) { case RLL_CAUSE_SEQ_ERR: case RLL_CAUSE_UNSOL_DM_RESP_MF: break; default: LOGP(DRR, LOGL_NOTICE, "MDL-Error (cause %d) ignoring\n", cause); return 0; } LOGP(DRR, LOGL_NOTICE, "MDL-Error (cause %d) aborting\n", cause); /* 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] = 1; /* local release */ gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, link_id); /* in case of modify/hando: wait for confirm */ 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) return -ENOMEM; nrrh = (struct gsm48_rr_hdr *)nmsg->data; nrrh->cause = RR_REL_CAUSE_LINK_FAILURE; nrrh->sapi = link_id & 7; gsm48_rr_upmsg(ms, nmsg); /* only for main signalling link */ if ((link_id & 7) == 0) { /* return idle */ new_rr_state(rr, GSM48_RR_ST_IDLE); /* release SAPI 3 link, if exits */ gsm48_release_sapi3_link(ms); } else { new_sapi3_state(rr, GSM48_RR_SAPI3ST_IDLE); LOGP(DSUM, LOGL_INFO, "Radio link SAPI3 failed\n"); } return 0; } static int gsm48_rr_estab_ind_sapi3(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); uint8_t link_id = rllh->link_id; struct msgb *nmsg; struct gsm48_rr_hdr *nrrh; if (rr->state != GSM48_RR_ST_DEDICATED) { /* disconnect sapi 3 link */ gsm48_release_sapi3_link(ms); return -EINVAL; } new_sapi3_state(rr, GSM48_RR_SAPI3ST_ESTAB); rr->sapi3_link_id = link_id; /* set link ID */ LOGP(DSUM, LOGL_INFO, "Radio link SAPI3 is established\n"); if ((link_id & 0xf8) == 0x00) { /* raise T200 of SAPI 0 */ ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_sec = T200_DCCH_SHARED; ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_usec= 0; } /* send inication to upper layer */ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_EST_IND); if (!nmsg) return -ENOMEM; nrrh = (struct gsm48_rr_hdr *)nmsg->data; nrrh->sapi = link_id & 7; return gsm48_rr_upmsg(ms, nmsg); } static int gsm48_rr_estab_cnf_sapi3(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); uint8_t link_id = rllh->link_id; struct msgb *nmsg; struct gsm48_rr_hdr *nrrh; if (rr->state != GSM48_RR_ST_DEDICATED) { gsm48_release_sapi3_link(ms); return -EINVAL; } new_sapi3_state(rr, GSM48_RR_SAPI3ST_ESTAB); rr->sapi3_link_id = link_id; /* set link ID, just to be sure */ LOGP(DSUM, LOGL_INFO, "Radio link SAPI3 is established\n"); /* send inication to upper layer */ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_EST_CNF); if (!nmsg) return -ENOMEM; nrrh = (struct gsm48_rr_hdr *)nmsg->data; nrrh->sapi = link_id & 7; return gsm48_rr_upmsg(ms, nmsg); } /* 3.4.2 data from layer 2 to RR and upper layer (sapi 3)*/ static int gsm48_rr_data_ind_sapi3(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; if (pdisc == GSM48_PDISC_RR) { msgb_free(msg); return -EINVAL; } /* pull off RSL header up to L3 message */ msgb_pull(msg, (long)msgb_l3(msg) - (long)msg->data); /* push RR header */ 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); } static int gsm48_rr_rel_ind_sapi3(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); uint8_t link_id = rllh->link_id; struct msgb *nmsg; struct gsm48_rr_hdr *nrrh; new_sapi3_state(rr, GSM48_RR_SAPI3ST_IDLE); LOGP(DSUM, LOGL_INFO, "Radio link SAPI3 is released\n"); /* lower T200 of SAPI 0 */ ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_sec = T200_DCCH; ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_usec = 0; /* send inication to upper layer */ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); if (!nmsg) return -ENOMEM; nrrh = (struct gsm48_rr_hdr *)nmsg->data; nrrh->cause = RR_REL_CAUSE_NORMAL; nrrh->sapi = link_id & 7; return gsm48_rr_upmsg(ms, nmsg); } /* request SAPI 3 establishment */ static int gsm48_rr_est_req_sapi3(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; uint8_t ch_type, ch_subch, ch_ts; struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *) msg->data; uint8_t sapi = rrh->sapi; struct msgb *nmsg; if (rr->state != GSM48_RR_ST_DEDICATED) { struct gsm48_rr_hdr *nrrh; /* send inication to upper layer */ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); if (!nmsg) return -ENOMEM; nrrh = (struct gsm48_rr_hdr *)nmsg->data; nrrh->cause = RR_REL_CAUSE_NORMAL; nrrh->sapi = sapi; return gsm48_rr_upmsg(ms, nmsg); } 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 " "(sapi %d)\n", sapi); rr->sapi3_link_id = 0x00 | sapi; /* SAPI 3, DCCH */ /* raise T200 of SAPI 0 */ ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_sec = T200_DCCH_SHARED; ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI0].dl.t200_usec= 0; } else { LOGP(DRR, LOGL_INFO, "Requesting ACCH link, because TCH " "(sapi %d)\n", sapi); rr->sapi3_link_id = 0x40 | sapi; /* SAPI 3, ACCH */ } /* already established */ new_sapi3_state(rr, GSM48_RR_SAPI3ST_WAIT_EST); /* send message */ nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; return gsm48_send_rsl_nol3(ms, RSL_MT_EST_REQ, nmsg, rr->sapi3_link_id); } static int gsm48_rr_est_req_estab_sapi3(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *) msg->data; uint8_t sapi = rrh->sapi; struct msgb *nmsg; struct gsm48_rr_hdr *nrrh; LOGP(DRR, LOGL_INFO, "Radio link SAPI3 already established\n"); /* send inication to upper layer */ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_EST_CNF); if (!nmsg) return -ENOMEM; nrrh = (struct gsm48_rr_hdr *)nmsg->data; nrrh->sapi = sapi; return gsm48_rr_upmsg(ms, nmsg); } /* * state machines */ /* state trasitions for link layer messages (lower layer) */ static struct dldatastate { uint32_t states; int type; int (*rout) (struct osmocom_ms *ms, struct msgb *msg); } dldatastatelist[] = { /* SAPI 0 on DCCH */ /* data transfer */ {SBIT(GSM48_RR_ST_IDLE) | SBIT(GSM48_RR_ST_CONN_PEND) | SBIT(GSM48_RR_ST_DEDICATED) | SBIT(GSM48_RR_ST_REL_PEND), RSL_MT_UNIT_DATA_IND, gsm48_rr_unit_data_ind}, {SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.2 */ RSL_MT_DATA_IND, gsm48_rr_data_ind}, /* esablish */ {SBIT(GSM48_RR_ST_IDLE) | SBIT(GSM48_RR_ST_CONN_PEND) | SBIT(GSM48_RR_ST_REL_PEND), RSL_MT_EST_CONF, gsm48_rr_estab_cnf}, /* resume */ {SBIT(GSM48_RR_ST_DEDICATED), RSL_MT_EST_CONF, gsm48_rr_estab_cnf_dedicated}, /* release */ {SBIT(GSM48_RR_ST_CONN_PEND) | SBIT(GSM48_RR_ST_DEDICATED), RSL_MT_REL_IND, gsm48_rr_rel_ind}, {SBIT(GSM48_RR_ST_REL_PEND), RSL_MT_REL_CONF, gsm48_rr_rel_cnf}, /* reconnect */ {SBIT(GSM48_RR_ST_CONN_PEND) | SBIT(GSM48_RR_ST_DEDICATED), RSL_MT_REL_CONF, gsm48_rr_rel_cnf}, /* suspenion */ {SBIT(GSM48_RR_ST_DEDICATED), RSL_MT_SUSP_CONF, gsm48_rr_susp_cnf_dedicated}, {SBIT(GSM48_RR_ST_DEDICATED), RSL_MT_ERROR_IND, gsm48_rr_mdl_error_ind}, }; #define DLDATASLLEN \ (sizeof(dldatastatelist) / sizeof(struct dldatastate)) static struct dldatastate dldatastatelists3[] = { /* SAPI 3 on DCCH */ /* establish */ {SBIT(GSM48_RR_SAPI3ST_IDLE), RSL_MT_EST_IND, gsm48_rr_estab_ind_sapi3}, /* establish */ {SBIT(GSM48_RR_SAPI3ST_IDLE) | SBIT(GSM48_RR_SAPI3ST_WAIT_EST), RSL_MT_EST_CONF, gsm48_rr_estab_cnf_sapi3}, /* data transfer */ {SBIT(GSM48_RR_SAPI3ST_ESTAB), RSL_MT_DATA_IND, gsm48_rr_data_ind_sapi3}, /* release */ {SBIT(GSM48_RR_SAPI3ST_WAIT_EST) | SBIT(GSM48_RR_SAPI3ST_ESTAB), RSL_MT_REL_IND, gsm48_rr_rel_ind_sapi3}, {ALL_STATES, RSL_MT_ERROR_IND, gsm48_rr_mdl_error_ind}, }; #define DLDATASLLENS3 \ (sizeof(dldatastatelists3) / sizeof(struct dldatastate)) static int gsm48_rcv_rll(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); int msg_type = rllh->c.msg_type; int link_id = rllh->link_id; int i; int rc; LOGP(DRSL, LOGL_INFO, "(ms %s) Received '%s' from L2 in state " "%s (link_id 0x%x)\n", ms->name, rsl_msg_name(msg_type), gsm48_rr_state_names[rr->state], link_id); /* find function for current state and message */ if (!(link_id & 7)) { /* SAPI 0 */ for (i = 0; i < DLDATASLLEN; i++) if ((msg_type == dldatastatelist[i].type) && ((1 << rr->state) & dldatastatelist[i].states)) break; if (i == DLDATASLLEN) { LOGP(DRSL, LOGL_NOTICE, "RSLms message '%s' " "unhandled\n", rsl_msg_name(msg_type)); msgb_free(msg); return 0; } rc = dldatastatelist[i].rout(ms, msg); } else { /* SAPI 3 */ for (i = 0; i < DLDATASLLENS3; i++) if ((msg_type == dldatastatelists3[i].type) && ((1 << rr->sapi3_state) & dldatastatelists3[i].states)) break; if (i == DLDATASLLENS3) { LOGP(DRSL, LOGL_NOTICE, "RSLms message '%s' " "unhandled\n", rsl_msg_name(msg_type)); msgb_free(msg); return 0; } rc = dldatastatelists3[i].rout(ms, msg); } /* free msgb unless it is forwarded */ if (msg_type != RSL_MT_DATA_IND) msgb_free(msg); return rc; } static int gsm48_rcv_cch(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct abis_rsl_cchan_hdr *ch = msgb_l2(msg); int msg_type = ch->c.msg_type; int rc; LOGP(DRSL, LOGL_INFO, "(ms %s) Received '%s' from L2 in state " "%s\n", ms->name, rsl_msg_name(msg_type), gsm48_rr_state_names[rr->state]); 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"); msgb_free(msg); return 0; } /* 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); int rc = 0; switch (rslh->msg_discr & 0xfe) { case ABIS_RSL_MDISC_RLL: rc = gsm48_rcv_rll(ms, msg); break; case ABIS_RSL_MDISC_COM_CHAN: rc = gsm48_rcv_cch(ms, msg); break; default: /* FIXME: implement this */ LOGP(DRSL, LOGL_NOTICE, "unknown RSLms msg_discr 0x%02x\n", rslh->msg_discr); msgb_free(msg); rc = -EINVAL; break; } return rc; } /* state trasitions for RR-SAP messages from up (main link) */ static struct rrdownstate { uint32_t states; int type; int (*rout) (struct osmocom_ms *ms, struct msgb *msg); } rrdownstatelist[] = { /* SAPI 0 */ /* NOTE: If not IDLE, it is rejected there. */ {ALL_STATES, /* 3.3.1.1 */ GSM48_RR_EST_REQ, gsm48_rr_est_req}, {SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.2 */ GSM48_RR_DATA_REQ, gsm48_rr_data_req}, {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 \ (sizeof(rrdownstatelist) / sizeof(struct rrdownstate)) /* state trasitions for RR-SAP messages from up with (SAPI 3) */ static struct rrdownstate rrdownstatelists3[] = { /* SAPI 3 */ {SBIT(GSM48_RR_SAPI3ST_IDLE), GSM48_RR_EST_REQ, gsm48_rr_est_req_sapi3}, {SBIT(GSM48_RR_SAPI3ST_ESTAB), GSM48_RR_EST_REQ, gsm48_rr_est_req_estab_sapi3}, {SBIT(GSM48_RR_SAPI3ST_ESTAB), GSM48_RR_DATA_REQ, gsm48_rr_data_req}, /* handles SAPI 3 too */ }; #define RRDOWNSLLENS3 \ (sizeof(rrdownstatelists3) / sizeof(struct rrdownstate)) int gsm48_rr_downmsg(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *) msg->data; int msg_type = rrh->msg_type; int sapi = rrh->sapi; int i; int rc; LOGP(DRR, LOGL_INFO, "(ms %s) Message '%s' received in state %s " "(sapi %d)\n", ms->name, get_rr_name(msg_type), gsm48_rr_state_names[rr->state], sapi); if (!sapi) { /* SAPI 0: find function for current state and message */ for (i = 0; i < RRDOWNSLLEN; i++) if ((msg_type == rrdownstatelist[i].type) && ((1 << rr->state) & rrdownstatelist[i].states)) break; if (i == RRDOWNSLLEN) { LOGP(DRR, LOGL_NOTICE, "Message unhandled at this " "state.\n"); msgb_free(msg); return 0; } rc = rrdownstatelist[i].rout(ms, msg); } else { /* SAPI 3: find function for current state and message */ for (i = 0; i < RRDOWNSLLENS3; i++) if ((msg_type == rrdownstatelists3[i].type) && ((1 << rr->sapi3_state) & rrdownstatelists3[i].states)) break; if (i == RRDOWNSLLENS3) { LOGP(DRR, LOGL_NOTICE, "Message unhandled at this " "state.\n"); msgb_free(msg); return 0; } rc = rrdownstatelists3[i].rout(ms, msg); } /* free msgb unless it is forwarded */ if (msg_type != GSM48_RR_DATA_REQ) msgb_free(msg); return rc; } /* * init/exit */ int gsm48_rr_init(struct osmocom_ms *ms) { struct gsm48_rrlayer *rr = &ms->rrlayer; memset(rr, 0, sizeof(*rr)); rr->ms = ms; LOGP(DRR, LOGL_INFO, "init Radio Ressource process\n"); INIT_LLIST_HEAD(&rr->rsl_upqueue); INIT_LLIST_HEAD(&rr->downqueue); /* downqueue is handled here, so don't add_work */ lapdm_channel_set_l3(&ms->lapdm_channel, &rcv_rsl, ms); start_rr_t_meas(rr, 1, 0); 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; } int gsm48_rr_exit(struct osmocom_ms *ms) { struct gsm48_rrlayer *rr = &ms->rrlayer; struct msgb *msg; LOGP(DRR, LOGL_INFO, "exit Radio Ressource process\n"); /* flush queues */ while ((msg = msgb_dequeue(&rr->rsl_upqueue))) msgb_free(msg); while ((msg = msgb_dequeue(&rr->downqueue))) msgb_free(msg); if (rr->rr_est_msg) { msgb_free(rr->rr_est_msg); rr->rr_est_msg = NULL; } stop_rr_t_meas(rr); stop_rr_t_starting(rr); stop_rr_t_rel_wait(rr); stop_rr_t3110(rr); 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; } #if 0 todo rr_sync_ind when receiving ciph, re ass, channel mode modify static void timeout_rr_t3124(void *arg) { struct gsm48_rrlayer *rr = arg; struct msgb *nmsg; /* stop sending more access bursts when timer expired */ hando_acc_left = 0; /* get old channel description */ memcpy(&rr->chan_desc, &rr->chan_last, sizeof(rr->chan_desc)); /* change radio to old channel */ tx_ph_dm_est_req(ms, rr->cd_now.arfcn, rr->cd_now.chan_nr, rr->cd_now.tsc); rr->dm_est = 1; /* re-establish old link */ nmsg = gsm48_l3_msgb_alloc(); if (!nmsg) return -ENOMEM; return gsm48_send_rsl(ms, RSL_MT_REEST_REQ, nmsg, 0); todo } static void start_rr_t3124(struct gsm48_rrlayer *rr, int sec, int micro) { LOGP(DRR, LOGL_INFO, "starting T3124 with %d.%03d seconds\n", sec, micro / 1000); rr->t3124.cb = timeout_rr_t3124; rr->t3124.data = rr; osmo_timer_schedule(&rr->t3124, sec, micro); } #endif 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"); goto error; } 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) { struct gsm48_rrlayer *rr = &ms->rrlayer; uint8_t ch_type, ch_subch, ch_ts; LOGP(DRR, LOGL_INFO, "setting audio mode to %d\n", mode); rr->audio_mode = mode; if (!rr->dm_est) return 0; 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, rr->cd_now.tch_flags, rr->tch_loop_mode); }