summaryrefslogtreecommitdiffstats
path: root/src/host
diff options
context:
space:
mode:
authorVadim Yanitskiy <vyanitskiy@sysmocom.de>2023-03-03 19:23:41 +0700
committerfixeria <vyanitskiy@sysmocom.de>2023-03-14 15:06:26 +0000
commit8240ef74fc386fd95ca0b669e99ee7d0041d2b83 (patch)
treedd1558e25118cfcf2fd2c37e86446d89deef5c43 /src/host
parentb8c3bef7376fab28d2f71406e4b4e9c3fcddfcf0 (diff)
modem: move GRR specific code into its own file
Diffstat (limited to 'src/host')
-rw-r--r--src/host/layer23/include/osmocom/bb/modem/Makefile.am1
-rw-r--r--src/host/layer23/include/osmocom/bb/modem/grr.h6
-rw-r--r--src/host/layer23/src/modem/Makefile.am1
-rw-r--r--src/host/layer23/src/modem/app_modem.c420
-rw-r--r--src/host/layer23/src/modem/grr.c455
5 files changed, 465 insertions, 418 deletions
diff --git a/src/host/layer23/include/osmocom/bb/modem/Makefile.am b/src/host/layer23/include/osmocom/bb/modem/Makefile.am
index 4acb21b0..b2e30590 100644
--- a/src/host/layer23/include/osmocom/bb/modem/Makefile.am
+++ b/src/host/layer23/include/osmocom/bb/modem/Makefile.am
@@ -1,5 +1,6 @@
noinst_HEADERS = \
modem.h \
+ grr.h \
llc.h \
rlcmac.h \
sndcp.h \
diff --git a/src/host/layer23/include/osmocom/bb/modem/grr.h b/src/host/layer23/include/osmocom/bb/modem/grr.h
new file mode 100644
index 00000000..208dbb1f
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/modem/grr.h
@@ -0,0 +1,6 @@
+#pragma once
+
+struct msgb;
+struct lapdm_entity;
+
+int modem_grr_rslms_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx);
diff --git a/src/host/layer23/src/modem/Makefile.am b/src/host/layer23/src/modem/Makefile.am
index 7ce71dc9..500b0315 100644
--- a/src/host/layer23/src/modem/Makefile.am
+++ b/src/host/layer23/src/modem/Makefile.am
@@ -18,6 +18,7 @@ bin_PROGRAMS = modem
modem_SOURCES = \
$(top_srcdir)/src/common/main.c \
app_modem.c \
+ grr.c \
llc.c \
rlcmac.c \
sndcp.c \
diff --git a/src/host/layer23/src/modem/app_modem.c b/src/host/layer23/src/modem/app_modem.c
index 65381141..1ff4dbfb 100644
--- a/src/host/layer23/src/modem/app_modem.c
+++ b/src/host/layer23/src/modem/app_modem.c
@@ -33,26 +33,18 @@
#include <osmocom/core/tun.h>
#include <osmocom/vty/vty.h>
-#include <osmocom/gsm/rsl.h>
-#include <osmocom/gsm/tlv.h>
-#include <osmocom/gsm/gsm48_ie.h>
-#include <osmocom/gsm/gsm48.h>
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include <osmocom/gprs/rlcmac/csn1_defs.h>
-#include <osmocom/gprs/rlcmac/rlcmac_prim.h>
-
#include <osmocom/bb/common/osmocom_data.h>
#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/l1ctl.h>
#include <osmocom/bb/common/l23_app.h>
#include <osmocom/bb/common/l1l2_interface.h>
-#include <osmocom/bb/common/sysinfo.h>
#include <osmocom/bb/common/apn.h>
#include <osmocom/bb/modem/rlcmac.h>
#include <osmocom/bb/modem/llc.h>
#include <osmocom/bb/modem/sndcp.h>
#include <osmocom/bb/modem/vty.h>
+#include <osmocom/bb/modem/grr.h>
#include <l1ctl_proto.h>
@@ -108,414 +100,6 @@ free_ret:
return rc;
}
-/* Generate a 8-bit CHANNEL REQUEST message as per 3GPP TS 44.018, 9.1.8 */
-static uint8_t gen_chan_req(bool single_block)
-{
- uint8_t rnd = (uint8_t)rand();
-
- if (single_block) /* 01110xxx */
- return 0x70 | (rnd & 0x07);
-
- /* 011110xx or 01111x0x or 01111xx0 */
- if ((rnd & 0x07) == 0x07)
- return 0x78;
- return 0x78 | (rnd & 0x07);
-}
-
-static int modem_tx_chan_req(struct osmocom_ms *ms, bool single_block)
-{
- struct gsm322_cellsel *cs = &ms->cellsel;
- struct gsm48_rrlayer *rr = &ms->rrlayer;
-
- OSMO_ASSERT(rr->state == GSM48_RR_ST_IDLE);
-
- if (!cs->sel_si.si1 || !cs->sel_si.si13)
- return -EAGAIN;
- if (!cs->sel_si.gprs.supported)
- return -ENOTSUP;
-
- rr->cr_ra = gen_chan_req(single_block);
- memset(&rr->cr_hist[0], 0x00, sizeof(rr->cr_hist));
-
- LOGP(DRR, LOGL_NOTICE, "Sending CHANNEL REQUEST (0x%02x)\n", rr->cr_ra);
- l1ctl_tx_rach_req(ms, RSL_CHAN_RACH, 0x00, rr->cr_ra, 0,
- cs->ccch_mode == CCCH_MODE_COMBINED);
-
- rr->state = GSM48_RR_ST_CONN_PEND;
- return 0;
-}
-
-static bool modem_match_req_ref(struct osmocom_ms *ms,
- const struct gsm48_req_ref *ref)
-{
- struct gsm48_rrlayer *rr = &ms->rrlayer;
-
- for (unsigned int i = 0; i < ARRAY_SIZE(rr->cr_hist); i++) {
- const struct gsm48_cr_hist *hist = &rr->cr_hist[i];
- if (!hist->valid)
- continue;
- if (memcmp(&hist->ref, ref, sizeof(*ref)) == 0)
- return true;
- }
-
- return false;
-}
-
-static int handle_si1(struct osmocom_ms *ms, struct msgb *msg)
-{
- struct gsm322_cellsel *cs = &ms->cellsel;
- int rc;
-
- if (msgb_l3len(msg) != GSM_MACBLOCK_LEN)
- return -EINVAL;
- if (!memcmp(&cs->sel_si.si1_msg[0], msgb_l3(msg), msgb_l3len(msg)))
- return 0; /* this message is already handled */
-
- rc = gsm48_decode_sysinfo1(&cs->sel_si, msgb_l3(msg), msgb_l3len(msg));
- if (rc != 0) {
- LOGP(DRR, LOGL_ERROR, "Failed to decode SI1 message\n");
- return rc;
- }
-
- return 0;
-}
-
-static int handle_si3(struct osmocom_ms *ms, struct msgb *msg)
-{
- struct gsm322_cellsel *cs = &ms->cellsel;
- int rc;
-
- if (msgb_l3len(msg) != GSM_MACBLOCK_LEN)
- return -EINVAL;
- if (!memcmp(&cs->sel_si.si3_msg[0], msgb_l3(msg), msgb_l3len(msg)))
- return 0; /* this message is already handled */
-
- rc = gsm48_decode_sysinfo3(&cs->sel_si, msgb_l3(msg), msgb_l3len(msg));
- if (rc != 0) {
- LOGP(DRR, LOGL_ERROR, "Failed to decode SI3 message\n");
- return rc;
- }
-
- if (cs->ccch_mode == CCCH_MODE_NONE) {
- if (cs->sel_si.ccch_conf == RSL_BCCH_CCCH_CONF_1_C)
- cs->ccch_mode = CCCH_MODE_COMBINED;
- else
- cs->ccch_mode = CCCH_MODE_NON_COMBINED;
- l1ctl_tx_ccch_mode_req(ms, cs->ccch_mode);
- }
-
- if (!cs->sel_si.gprs.supported) {
- LOGP(DRR, LOGL_NOTICE, "SI3 Rest Octets IE contains no GPRS Indicator\n");
- return 0;
- }
-
- LOGP(DRR, LOGL_NOTICE, "Found GPRS Indicator (RA Colour %u, SI13 on BCCH %s)\n",
- cs->sel_si.gprs.ra_colour, cs->sel_si.gprs.si13_pos ? "Ext" : "Norm");
-
- return 0;
-}
-
-static int handle_si4(struct osmocom_ms *ms, struct msgb *msg)
-{
- struct gsm322_cellsel *cs = &ms->cellsel;
- int rc;
-
- if (msgb_l3len(msg) != GSM_MACBLOCK_LEN)
- return -EINVAL;
- if (!memcmp(&cs->sel_si.si4_msg[0], msgb_l3(msg), msgb_l3len(msg)))
- return 0; /* this message is already handled */
-
- rc = gsm48_decode_sysinfo4(&cs->sel_si, msgb_l3(msg), msgb_l3len(msg));
- if (rc != 0) {
- LOGP(DRR, LOGL_ERROR, "Failed to decode SI4 message\n");
- return rc;
- }
-
- if (!cs->sel_si.gprs.supported) {
- LOGP(DRR, LOGL_NOTICE, "SI4 Rest Octets IE contains no GPRS Indicator\n");
- return 0;
- }
-
- LOGP(DRR, LOGL_NOTICE, "Found GPRS Indicator (RA Colour %u, SI13 on BCCH %s)\n",
- cs->sel_si.gprs.ra_colour, cs->sel_si.gprs.si13_pos ? "Ext" : "Norm");
-
- return 0;
-}
-
-static int handle_si13(struct osmocom_ms *ms, struct msgb *msg)
-{
- struct osmo_gprs_rlcmac_prim *rlcmac_prim;
- struct gsm322_cellsel *cs = &ms->cellsel;
- int rc;
-
- if (msgb_l3len(msg) != GSM_MACBLOCK_LEN)
- return -EINVAL;
- if (!memcmp(&cs->sel_si.si13_msg[0], msgb_l3(msg), msgb_l3len(msg)))
- return 0; /* this message is already handled */
-
- rc = gsm48_decode_sysinfo13(&cs->sel_si, msgb_l3(msg), msgb_l3len(msg));
- if (rc != 0)
- return rc;
-
- /* Forward SI13 to RLC/MAC layer */
- rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0 /* TODO: fn */, msgb_l3(msg));
- rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim);
- return rc;
-}
-
-static int modem_rx_bcch(struct osmocom_ms *ms, struct msgb *msg)
-{
- const struct gsm48_system_information_type_header *si_hdr = msgb_l3(msg);
- const uint8_t si_type = si_hdr->system_information;
-
- LOGP(DRR, LOGL_INFO, "BCCH message (type=0x%02x): %s\n",
- si_type, gsm48_rr_msg_name(si_type));
-
- /* HACK: request an Uplink TBF here (one phase access) */
- if (ms->rrlayer.state == GSM48_RR_ST_IDLE)
- modem_tx_chan_req(ms, false);
-
- switch (si_type) {
- case GSM48_MT_RR_SYSINFO_1:
- return handle_si1(ms, msg);
- case GSM48_MT_RR_SYSINFO_3:
- return handle_si3(ms, msg);
- case GSM48_MT_RR_SYSINFO_4:
- return handle_si4(ms, msg);
- case GSM48_MT_RR_SYSINFO_13:
- return handle_si13(ms, msg);
- default:
- return 0;
- };
-}
-
-static int modem_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
-{
- const struct gsm48_imm_ass *ia = msgb_l3(msg);
- struct gsm48_rrlayer *rr = &ms->rrlayer;
- uint8_t ch_type, ch_subch, ch_ts;
- int rc;
- struct osmo_gprs_rlcmac_prim *rlcmac_prim;
-
- /* Discard CS channel assignment */
- if ((ia->page_mode >> 4) == 0)
- return 0;
-
- if (rr->state != GSM48_RR_ST_CONN_PEND)
- return 0;
- if (!modem_match_req_ref(ms, &ia->req_ref))
- return 0;
-
- if (rsl_dec_chan_nr(ia->chan_desc.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__, ia->chan_desc.chan_nr);
- return -EINVAL;
- }
-
- if (!ia->chan_desc.h0.h) {
- /* Non-hopping */
- uint16_t arfcn;
-
- arfcn = ia->chan_desc.h0.arfcn_low | (ia->chan_desc.h0.arfcn_high << 8);
-
- LOGP(DRR, LOGL_INFO, "GSM48 IMM ASS (ra=0x%02x, chan_nr=0x%02x, "
- "ARFCN=%u, TS=%u, SS=%u, TSC=%u)\n", ia->req_ref.ra,
- ia->chan_desc.chan_nr, arfcn, ch_ts, ch_subch,
- ia->chan_desc.h0.tsc);
-
- l1ctl_tx_dm_est_req_h0(ms, arfcn,
- RSL_CHAN_OSMO_PDCH | ch_ts,
- ia->chan_desc.h0.tsc, GSM48_CMODE_SIGN, 0);
- } else {
- /* Hopping */
- uint8_t ma_len = 0;
- uint8_t maio, hsn;
- uint16_t ma[64];
-
- hsn = ia->chan_desc.h1.hsn;
- maio = ia->chan_desc.h1.maio_low | (ia->chan_desc.h1.maio_high << 2);
-
- LOGP(DRR, LOGL_INFO, "GSM48 IMM ASS (ra=0x%02x, chan_nr=0x%02x, "
- "HSN=%u, MAIO=%u, TS=%u, SS=%u, TSC=%u)\n", ia->req_ref.ra,
- ia->chan_desc.chan_nr, hsn, maio, ch_ts, ch_subch,
- ia->chan_desc.h1.tsc);
-
- for (unsigned int i = 1, j = 0; i <= 1024; i++) {
- unsigned int arfcn = i & 1023;
- unsigned int k;
-
- if (~ms->cellsel.sel_si.freq[arfcn].mask & 0x01)
- continue;
-
- k = ia->mob_alloc_len - (j >> 3) - 1;
- if (ia->mob_alloc[k] & (1 << (j & 7)))
- ma[ma_len++] = arfcn;
- j++;
- }
-
- l1ctl_tx_dm_est_req_h1(ms, maio, hsn, &ma[0], ma_len,
- RSL_CHAN_OSMO_PDCH | ch_ts,
- ia->chan_desc.h1.tsc, GSM48_CMODE_SIGN, 0);
- }
-
- rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0 /* TODO: fn */, (uint8_t *)ia);
- rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim);
- if (rc < 0)
- return rc;
-
- rr->state = GSM48_RR_ST_DEDICATED;
- return 0;
-}
-
-/* Dummy Paging Request 1 with "no identity" */
-static const uint8_t paging_fill[] = {
- 0x15, 0x06, 0x21, 0x00, 0x01, 0xf0, 0x2b,
- /* The rest part may be randomized */
-};
-
-/* LAPDm func=UI fill frame (for the BTS side) */
-static const uint8_t lapdm_fill[] = {
- 0x03, 0x03, 0x01, 0x2b,
- /* The rest part may be randomized */
-};
-
-/* TODO: share / generalize this code */
-static bool is_fill_frame(const struct msgb *msg)
-{
- const uint8_t *l2 = msgb_l3(msg);
-
- if (!memcmp(l2, paging_fill, sizeof(paging_fill)))
- return true;
- if (!memcmp(l2, lapdm_fill, sizeof(lapdm_fill)))
- return true;
-
- return false;
-}
-
-static int modem_rx_ccch(struct osmocom_ms *ms, struct msgb *msg)
-{
- const struct gsm48_system_information_type_header *sih = msgb_l3(msg);
-
- /* Skip frames with wrong length */
- if (msgb_l3len(msg) != GSM_MACBLOCK_LEN) {
- LOGP(DRR, LOGL_ERROR, "Rx CCCH message with odd length=%u: %s\n",
- msgb_l3len(msg), msgb_hexdump_l3(msg));
- return -EINVAL;
- }
-
- /* Skip dummy (fill) frames */
- if (is_fill_frame(msg))
- return 0;
-
- if (sih->rr_protocol_discriminator != GSM48_PDISC_RR) {
- LOGP(DRR, LOGL_ERROR, "PCH pdisc (%s) != RR\n",
- gsm48_pdisc_name(sih->rr_protocol_discriminator));
- }
-
- switch (sih->system_information) {
- case GSM48_MT_RR_IMM_ASS:
- return modem_rx_imm_ass(ms, msg);
- default:
- return 0;
- }
-}
-
-static int modem_rx_rslms_rll_ud(struct osmocom_ms *ms, struct msgb *msg)
-{
- const struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
- struct tlv_parsed tv;
-
- 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;
- }
-
- if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
- LOGP(DRSL, LOGL_ERROR, "UNIT_DATA_IND without L3 INFO ?!?\n");
- return -EINVAL;
- }
-
- msg->l3h = (uint8_t *)TLVP_VAL(&tv, RSL_IE_L3_INFO);
-
- switch (rllh->chan_nr) {
- case RSL_CHAN_PCH_AGCH:
- return modem_rx_ccch(ms, msg);
- case RSL_CHAN_BCCH:
- return modem_rx_bcch(ms, msg);
- default:
- return 0;
- }
-}
-
-static int modem_rx_rslms_rll(struct osmocom_ms *ms, struct msgb *msg)
-{
- const struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
-
- switch (rllh->c.msg_type) {
- case RSL_MT_UNIT_DATA_IND:
- return modem_rx_rslms_rll_ud(ms, msg);
- default:
- LOGP(DRSL, LOGL_NOTICE, "Unhandled RSLms RLL message "
- "(msg_type 0x%02x)\n", rllh->c.msg_type);
- return -EINVAL;
- }
-}
-
-static int modem_rx_rslms_cchan(struct osmocom_ms *ms, struct msgb *msg)
-{
- const struct abis_rsl_cchan_hdr *ch = msgb_l2(msg);
- struct gsm48_rrlayer *rr = &ms->rrlayer;
-
- switch (ch->c.msg_type) {
- case RSL_MT_CHAN_CONF: /* RACH.conf */
- if (rr->state == GSM48_RR_ST_CONN_PEND) {
- const struct gsm48_req_ref *ref = (void *)&ch->data[1];
- LOGP(DRSL, LOGL_NOTICE,
- "Rx RACH.conf (RA=0x%02x, T1=%u, T3=%u, T2=%u)\n",
- rr->cr_ra, ref->t1, ref->t3_high << 3 | ref->t3_low, ref->t2);
- /* shift the CHANNEL REQUEST history buffer */
- memmove(&rr->cr_hist[1], &rr->cr_hist[0], ARRAY_SIZE(rr->cr_hist) - 1);
- /* store the new entry */
- rr->cr_hist[0].ref = *ref;
- rr->cr_hist[0].ref.ra = rr->cr_ra;
- rr->cr_hist[0].valid = 1;
- return 0;
- }
- /* fall-through */
- default:
- LOGP(DRSL, LOGL_NOTICE, "Unhandled RSLms CCHAN message "
- "(msg_type 0x%02x)\n", ch->c.msg_type);
- return -EINVAL;
- }
-}
-
-static int modem_rslms_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx)
-{
- const struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
- int rc;
-
- switch (rslh->msg_discr & 0xfe) {
- case ABIS_RSL_MDISC_RLL:
- rc = modem_rx_rslms_rll((struct osmocom_ms *)ctx, msg);
- break;
- case ABIS_RSL_MDISC_COM_CHAN:
- rc = modem_rx_rslms_cchan((struct osmocom_ms *)ctx, msg);
- break;
- default:
- LOGP(DRSL, LOGL_NOTICE, "Unhandled RSLms message "
- "(msg_discr 0x%02x)\n", rslh->msg_discr);
- rc = -EINVAL;
- break;
- }
-
- msgb_free(msg);
- return rc;
-}
-
void layer3_app_reset(void)
{
memset(&app_data, 0x00, sizeof(app_data));
@@ -584,7 +168,7 @@ int l23_app_init(void)
}
osmo_signal_register_handler(SS_L1CTL, &signal_cb, NULL);
- lapdm_channel_set_l3(&app_data.ms->lapdm_channel, &modem_rslms_cb, app_data.ms);
+ lapdm_channel_set_l3(&app_data.ms->lapdm_channel, &modem_grr_rslms_cb, app_data.ms);
return 0;
}
diff --git a/src/host/layer23/src/modem/grr.c b/src/host/layer23/src/modem/grr.c
new file mode 100644
index 00000000..e46f77db
--- /dev/null
+++ b/src/host/layer23/src/modem/grr.c
@@ -0,0 +1,455 @@
+/*
+ * (C) 2022-2023 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/lienses/>.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/gsm/rsl.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/lapdm.h>
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/gsm/gsm48_ie.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+#include <osmocom/gprs/rlcmac/rlcmac_prim.h>
+
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/sysinfo.h>
+#include <osmocom/bb/common/l1ctl.h>
+#include <osmocom/bb/common/ms.h>
+
+#include <osmocom/bb/mobile/gsm322.h>
+#include <osmocom/bb/mobile/gsm48_rr.h>
+
+#include <l1ctl_proto.h>
+
+/* Generate an 8-bit CHANNEL REQUEST message as per 3GPP TS 44.018, 9.1.8 */
+static uint8_t grr_gen_chan_req(bool single_block)
+{
+ uint8_t rnd = (uint8_t)rand();
+
+ if (single_block) /* 01110xxx */
+ return 0x70 | (rnd & 0x07);
+
+ /* 011110xx or 01111x0x or 01111xx0 */
+ if ((rnd & 0x07) == 0x07)
+ return 0x78;
+ return 0x78 | (rnd & 0x07);
+}
+
+static bool grr_match_req_ref(struct osmocom_ms *ms,
+ const struct gsm48_req_ref *ref)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(rr->cr_hist); i++) {
+ const struct gsm48_cr_hist *hist = &rr->cr_hist[i];
+ if (!hist->valid)
+ continue;
+ if (memcmp(&hist->ref, ref, sizeof(*ref)) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+static int grr_tx_chan_req(struct osmocom_ms *ms, bool single_block)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+ OSMO_ASSERT(rr->state == GSM48_RR_ST_IDLE);
+
+ if (!cs->sel_si.si1 || !cs->sel_si.si13)
+ return -EAGAIN;
+ if (!cs->sel_si.gprs.supported)
+ return -ENOTSUP;
+
+ rr->cr_ra = grr_gen_chan_req(single_block);
+ memset(&rr->cr_hist[0], 0x00, sizeof(rr->cr_hist));
+
+ LOGP(DRR, LOGL_NOTICE, "Sending CHANNEL REQUEST (0x%02x)\n", rr->cr_ra);
+ l1ctl_tx_rach_req(ms, RSL_CHAN_RACH, 0x00, rr->cr_ra, 0,
+ cs->ccch_mode == CCCH_MODE_COMBINED);
+
+ rr->state = GSM48_RR_ST_CONN_PEND;
+ return 0;
+}
+
+static int grr_handle_si1(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ int rc;
+
+ if (msgb_l3len(msg) != GSM_MACBLOCK_LEN)
+ return -EINVAL;
+ if (!memcmp(&cs->sel_si.si1_msg[0], msgb_l3(msg), msgb_l3len(msg)))
+ return 0; /* this message is already handled */
+
+ rc = gsm48_decode_sysinfo1(&cs->sel_si, msgb_l3(msg), msgb_l3len(msg));
+ if (rc != 0) {
+ LOGP(DRR, LOGL_ERROR, "Failed to decode SI1 message\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static int grr_handle_si3(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ int rc;
+
+ if (msgb_l3len(msg) != GSM_MACBLOCK_LEN)
+ return -EINVAL;
+ if (!memcmp(&cs->sel_si.si3_msg[0], msgb_l3(msg), msgb_l3len(msg)))
+ return 0; /* this message is already handled */
+
+ rc = gsm48_decode_sysinfo3(&cs->sel_si, msgb_l3(msg), msgb_l3len(msg));
+ if (rc != 0) {
+ LOGP(DRR, LOGL_ERROR, "Failed to decode SI3 message\n");
+ return rc;
+ }
+
+ if (cs->ccch_mode == CCCH_MODE_NONE) {
+ if (cs->sel_si.ccch_conf == RSL_BCCH_CCCH_CONF_1_C)
+ cs->ccch_mode = CCCH_MODE_COMBINED;
+ else
+ cs->ccch_mode = CCCH_MODE_NON_COMBINED;
+ l1ctl_tx_ccch_mode_req(ms, cs->ccch_mode);
+ }
+
+ if (!cs->sel_si.gprs.supported) {
+ LOGP(DRR, LOGL_NOTICE, "SI3 Rest Octets IE contains no GPRS Indicator\n");
+ return 0;
+ }
+
+ LOGP(DRR, LOGL_NOTICE, "Found GPRS Indicator (RA Colour %u, SI13 on BCCH %s)\n",
+ cs->sel_si.gprs.ra_colour, cs->sel_si.gprs.si13_pos ? "Ext" : "Norm");
+
+ return 0;
+}
+
+static int grr_handle_si4(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ int rc;
+
+ if (msgb_l3len(msg) != GSM_MACBLOCK_LEN)
+ return -EINVAL;
+ if (!memcmp(&cs->sel_si.si4_msg[0], msgb_l3(msg), msgb_l3len(msg)))
+ return 0; /* this message is already handled */
+
+ rc = gsm48_decode_sysinfo4(&cs->sel_si, msgb_l3(msg), msgb_l3len(msg));
+ if (rc != 0) {
+ LOGP(DRR, LOGL_ERROR, "Failed to decode SI4 message\n");
+ return rc;
+ }
+
+ if (!cs->sel_si.gprs.supported) {
+ LOGP(DRR, LOGL_NOTICE, "SI4 Rest Octets IE contains no GPRS Indicator\n");
+ return 0;
+ }
+
+ LOGP(DRR, LOGL_NOTICE, "Found GPRS Indicator (RA Colour %u, SI13 on BCCH %s)\n",
+ cs->sel_si.gprs.ra_colour, cs->sel_si.gprs.si13_pos ? "Ext" : "Norm");
+
+ return 0;
+}
+
+static int grr_handle_si13(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct osmo_gprs_rlcmac_prim *rlcmac_prim;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ int rc;
+
+ if (msgb_l3len(msg) != GSM_MACBLOCK_LEN)
+ return -EINVAL;
+ if (!memcmp(&cs->sel_si.si13_msg[0], msgb_l3(msg), msgb_l3len(msg)))
+ return 0; /* this message is already handled */
+
+ rc = gsm48_decode_sysinfo13(&cs->sel_si, msgb_l3(msg), msgb_l3len(msg));
+ if (rc != 0)
+ return rc;
+
+ /* Forward SI13 to RLC/MAC layer */
+ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0 /* TODO: fn */, msgb_l3(msg));
+ rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim);
+ return rc;
+}
+
+static int grr_rx_bcch(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct gsm48_system_information_type_header *si_hdr = msgb_l3(msg);
+ const uint8_t si_type = si_hdr->system_information;
+
+ LOGP(DRR, LOGL_INFO, "BCCH message (type=0x%02x): %s\n",
+ si_type, gsm48_rr_msg_name(si_type));
+
+ /* HACK: request an Uplink TBF here (one phase access) */
+ if (ms->rrlayer.state == GSM48_RR_ST_IDLE)
+ grr_tx_chan_req(ms, false);
+
+ switch (si_type) {
+ case GSM48_MT_RR_SYSINFO_1:
+ return grr_handle_si1(ms, msg);
+ case GSM48_MT_RR_SYSINFO_3:
+ return grr_handle_si3(ms, msg);
+ case GSM48_MT_RR_SYSINFO_4:
+ return grr_handle_si4(ms, msg);
+ case GSM48_MT_RR_SYSINFO_13:
+ return grr_handle_si13(ms, msg);
+ default:
+ return 0;
+ };
+}
+
+static int grr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct gsm48_imm_ass *ia = msgb_l3(msg);
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ uint8_t ch_type, ch_subch, ch_ts;
+ int rc;
+ struct osmo_gprs_rlcmac_prim *rlcmac_prim;
+
+ /* Discard CS channel assignment */
+ if ((ia->page_mode >> 4) == 0)
+ return 0;
+
+ if (rr->state != GSM48_RR_ST_CONN_PEND)
+ return 0;
+ if (!grr_match_req_ref(ms, &ia->req_ref))
+ return 0;
+
+ if (rsl_dec_chan_nr(ia->chan_desc.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__, ia->chan_desc.chan_nr);
+ return -EINVAL;
+ }
+
+ if (!ia->chan_desc.h0.h) {
+ /* Non-hopping */
+ uint16_t arfcn;
+
+ arfcn = ia->chan_desc.h0.arfcn_low | (ia->chan_desc.h0.arfcn_high << 8);
+
+ LOGP(DRR, LOGL_INFO, "GSM48 IMM ASS (ra=0x%02x, chan_nr=0x%02x, "
+ "ARFCN=%u, TS=%u, SS=%u, TSC=%u)\n", ia->req_ref.ra,
+ ia->chan_desc.chan_nr, arfcn, ch_ts, ch_subch,
+ ia->chan_desc.h0.tsc);
+
+ l1ctl_tx_dm_est_req_h0(ms, arfcn,
+ RSL_CHAN_OSMO_PDCH | ch_ts,
+ ia->chan_desc.h0.tsc, GSM48_CMODE_SIGN, 0);
+ } else {
+ /* Hopping */
+ uint8_t ma_len = 0;
+ uint8_t maio, hsn;
+ uint16_t ma[64];
+
+ hsn = ia->chan_desc.h1.hsn;
+ maio = ia->chan_desc.h1.maio_low | (ia->chan_desc.h1.maio_high << 2);
+
+ LOGP(DRR, LOGL_INFO, "GSM48 IMM ASS (ra=0x%02x, chan_nr=0x%02x, "
+ "HSN=%u, MAIO=%u, TS=%u, SS=%u, TSC=%u)\n", ia->req_ref.ra,
+ ia->chan_desc.chan_nr, hsn, maio, ch_ts, ch_subch,
+ ia->chan_desc.h1.tsc);
+
+ for (unsigned int i = 1, j = 0; i <= 1024; i++) {
+ unsigned int arfcn = i & 1023;
+ unsigned int k;
+
+ if (~ms->cellsel.sel_si.freq[arfcn].mask & 0x01)
+ continue;
+
+ k = ia->mob_alloc_len - (j >> 3) - 1;
+ if (ia->mob_alloc[k] & (1 << (j & 7)))
+ ma[ma_len++] = arfcn;
+ j++;
+ }
+
+ l1ctl_tx_dm_est_req_h1(ms, maio, hsn, &ma[0], ma_len,
+ RSL_CHAN_OSMO_PDCH | ch_ts,
+ ia->chan_desc.h1.tsc, GSM48_CMODE_SIGN, 0);
+ }
+
+ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0 /* TODO: fn */, (uint8_t *)ia);
+ rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim);
+ if (rc < 0)
+ return rc;
+
+ rr->state = GSM48_RR_ST_DEDICATED;
+ return 0;
+}
+
+/* Dummy Paging Request 1 with "no identity" */
+static const uint8_t paging_fill[] = {
+ 0x15, 0x06, 0x21, 0x00, 0x01, 0xf0, 0x2b,
+ /* The rest part may be randomized */
+};
+
+/* LAPDm func=UI fill frame (for the BTS side) */
+static const uint8_t lapdm_fill[] = {
+ 0x03, 0x03, 0x01, 0x2b,
+ /* The rest part may be randomized */
+};
+
+/* TODO: share / generalize this code */
+static bool is_fill_frame(const struct msgb *msg)
+{
+ const uint8_t *l2 = msgb_l3(msg);
+
+ if (!memcmp(l2, paging_fill, sizeof(paging_fill)))
+ return true;
+ if (!memcmp(l2, lapdm_fill, sizeof(lapdm_fill)))
+ return true;
+
+ return false;
+}
+
+static int grr_rx_ccch(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct gsm48_system_information_type_header *sih = msgb_l3(msg);
+
+ /* Skip frames with wrong length */
+ if (msgb_l3len(msg) != GSM_MACBLOCK_LEN) {
+ LOGP(DRR, LOGL_ERROR, "Rx CCCH message with odd length=%u: %s\n",
+ msgb_l3len(msg), msgb_hexdump_l3(msg));
+ return -EINVAL;
+ }
+
+ /* Skip dummy (fill) frames */
+ if (is_fill_frame(msg))
+ return 0;
+
+ if (sih->rr_protocol_discriminator != GSM48_PDISC_RR) {
+ LOGP(DRR, LOGL_ERROR, "PCH pdisc (%s) != RR\n",
+ gsm48_pdisc_name(sih->rr_protocol_discriminator));
+ }
+
+ switch (sih->system_information) {
+ case GSM48_MT_RR_IMM_ASS:
+ return grr_rx_imm_ass(ms, msg);
+ default:
+ return 0;
+ }
+}
+
+static int grr_rx_rslms_rll_ud(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ struct tlv_parsed tv;
+
+ 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;
+ }
+
+ if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
+ LOGP(DRSL, LOGL_ERROR, "UNIT_DATA_IND without L3 INFO ?!?\n");
+ return -EINVAL;
+ }
+
+ msg->l3h = (uint8_t *)TLVP_VAL(&tv, RSL_IE_L3_INFO);
+
+ switch (rllh->chan_nr) {
+ case RSL_CHAN_PCH_AGCH:
+ return grr_rx_ccch(ms, msg);
+ case RSL_CHAN_BCCH:
+ return grr_rx_bcch(ms, msg);
+ default:
+ return 0;
+ }
+}
+
+static int grr_rx_rslms_rll(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+
+ switch (rllh->c.msg_type) {
+ case RSL_MT_UNIT_DATA_IND:
+ return grr_rx_rslms_rll_ud(ms, msg);
+ default:
+ LOGP(DRSL, LOGL_NOTICE, "Unhandled RSLms RLL message "
+ "(msg_type 0x%02x)\n", rllh->c.msg_type);
+ return -EINVAL;
+ }
+}
+
+static int grr_rx_rslms_cchan(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct abis_rsl_cchan_hdr *ch = msgb_l2(msg);
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+ switch (ch->c.msg_type) {
+ case RSL_MT_CHAN_CONF: /* RACH.conf */
+ if (rr->state == GSM48_RR_ST_CONN_PEND) {
+ const struct gsm48_req_ref *ref = (void *)&ch->data[1];
+ LOGP(DRSL, LOGL_NOTICE,
+ "Rx RACH.conf (RA=0x%02x, T1=%u, T3=%u, T2=%u)\n",
+ rr->cr_ra, ref->t1, ref->t3_high << 3 | ref->t3_low, ref->t2);
+ /* shift the CHANNEL REQUEST history buffer */
+ memmove(&rr->cr_hist[1], &rr->cr_hist[0], ARRAY_SIZE(rr->cr_hist) - 1);
+ /* store the new entry */
+ rr->cr_hist[0].ref = *ref;
+ rr->cr_hist[0].ref.ra = rr->cr_ra;
+ rr->cr_hist[0].valid = 1;
+ return 0;
+ }
+ /* fall-through */
+ default:
+ LOGP(DRSL, LOGL_NOTICE, "Unhandled RSLms CCHAN message "
+ "(msg_type 0x%02x)\n", ch->c.msg_type);
+ return -EINVAL;
+ }
+}
+
+int modem_grr_rslms_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx)
+{
+ const struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
+ int rc;
+
+ switch (rslh->msg_discr & 0xfe) {
+ case ABIS_RSL_MDISC_RLL:
+ rc = grr_rx_rslms_rll((struct osmocom_ms *)ctx, msg);
+ break;
+ case ABIS_RSL_MDISC_COM_CHAN:
+ rc = grr_rx_rslms_cchan((struct osmocom_ms *)ctx, msg);
+ break;
+ default:
+ LOGP(DRSL, LOGL_NOTICE, "Unhandled RSLms message "
+ "(msg_discr 0x%02x)\n", rslh->msg_discr);
+ rc = -EINVAL;
+ break;
+ }
+
+ msgb_free(msg);
+ return rc;
+}