laforge submitted this change.
ASCI: Add group receive mode support to RR layer
This allows reception of VGCS and VBS calls. A special sub-state is used
to differentiate between IDLE mode and group receive mode. Later it can
be used to differentiate between dedicated and group transmit mode.
Related: OS#5364
Change-Id: Ia7d806b354fb3be5729bff8ac9aa1c7ad7a8b539
---
M src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h
M src/host/layer23/src/mobile/gsm48_rr.c
2 files changed, 337 insertions(+), 5 deletions(-)
diff --git a/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h b/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h
index 2d0ab58..1429fd3 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h
@@ -11,6 +11,7 @@
#define T200_ACCH 2 /* SACCH SAPI 3 */
+
/* GSM 04.07 9.1.2 */
#define GSM48_RR_EST_REQ 0x10
#define GSM48_RR_EST_IND 0x12
@@ -81,6 +82,13 @@
#define GSM48_RR_ST_DEDICATED 2
#define GSM48_RR_ST_REL_PEND 3
+/* group states (VGCS) */
+enum gsm48_rr_gstate {
+ GSM48_RR_GST_OFF = 0,
+ GSM48_RR_GST_RECEIVE,
+ GSM48_RR_GST_TRANSMIT,
+};
+
/* special states for SAPI 3 link */
#define GSM48_RR_SAPI3ST_IDLE 0
#define GSM48_RR_SAPI3ST_WAIT_EST 1
@@ -211,6 +219,8 @@
/* group call */
struct {
struct llist_head notif_list; /* list of received call notifications */
+ enum gsm48_rr_gstate group_state; /* extension to RR state for group transmit/receive modes */
+ struct gsm48_rr_cd cd_group; /* channel description of group call channel */
} vgcs;
};
diff --git a/src/host/layer23/src/mobile/gsm48_rr.c b/src/host/layer23/src/mobile/gsm48_rr.c
index 38a029f..c78993b 100644
--- a/src/host/layer23/src/mobile/gsm48_rr.c
+++ b/src/host/layer23/src/mobile/gsm48_rr.c
@@ -34,6 +34,22 @@
* 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.
+ *
* New primitives are invented for group/broadcast calls. They are not
* specified in any recommendation. They are:
*
@@ -101,7 +117,11 @@
/* 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);
@@ -111,6 +131,7 @@
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);
/*
* support
@@ -366,19 +387,21 @@
static void new_rr_state(struct gsm48_rrlayer *rr, int state)
{
+ struct osmocom_ms *ms = rr->ms;
+
if (state < 0 || state >=
(sizeof(gsm48_rr_state_names) / sizeof(char *)))
return;
- /* must check against equal state */
- if (rr->state == state) {
+ /* Check against equal state or IDLE state. */
+ if (rr->state == state && state != GSM48_RR_ST_IDLE) {
LOGP(DRR, LOGL_INFO, "equal state ? %s\n",
gsm48_rr_state_names[rr->state]);
return;
}
LOGP(DRR, LOGL_INFO, "new state %s -> %s\n",
- gsm48_rr_state_names[rr->state], gsm48_rr_state_names[state]);
+ gsm48_rr_state_names[rr->state], gsm48_rr_state_names[state]);
/* abort handover, in case of release of dedicated mode */
if (rr->state == GSM48_RR_ST_DEDICATED) {
@@ -392,10 +415,15 @@
rr->state = state;
- if (state == GSM48_RR_ST_IDLE) {
+ if (state != GSM48_RR_ST_IDLE)
+ return;
+
+ /* Return from dedicated/group receive/transmit mode to idle mode. (Trigger cell reselection.) */
+ if (rr->vgcs.group_state == GSM48_RR_GST_OFF) {
struct msgb *msg, *nmsg;
struct gsm322_msg *em;
+ LOGP(DRR, LOGL_INFO, "Returning to IDLE mode.\n");
/* release dedicated mode, if any */
l1ctl_tx_dm_rel_req(rr->ms);
rr->ms->meas.rl_fail = 0;
@@ -438,6 +466,45 @@
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);
+#ifdef WITH_GAPK_IO
+ /* clean-up GAPK state */
+ gapk_io_clean_up_ms(rr->ms);
+#endif
+ return;
}
}
@@ -2331,6 +2398,15 @@
return gsm48_new_sysinfo(ms, si->system_information);
}
+/* Receive "SYSTEM INFORMATION TYPE 10" message (9.1.50). */
+static int gsm48_rr_rx_sysinfo_10(struct osmocom_ms *ms, struct msgb *msg)
+{
+ LOGP(DRR, LOGL_INFO, "SYSINFO 10\n");
+
+ /* Ignore content. */
+ return -ENOTSUP;
+}
+
/* receive "SYSTEM INFORMATION 13" message (9.1.43a) */
static int gsm48_rr_rx_sysinfo13(struct osmocom_ms *ms, struct msgb *msg)
{
@@ -3174,6 +3250,9 @@
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);
@@ -3708,6 +3787,30 @@
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);
+ }
+ tlv_parse(&tp, &gsm48_rr_att_tlvdef, cr->data, payload_len, 0, 0);
+
+ 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
*/
@@ -4850,6 +4953,13 @@
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) {
@@ -4937,10 +5047,185 @@
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);
+ if (gsm_refer_pcs(cs->arfcn, s))
+ cd.arfcn |= ARFCN_PCS;
+ 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;
+ }
+
+ /* 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;
+
+ /* 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;
+
+ /* 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;
+}
+
/* 3.4.2 transfer data in dedicated mode */
static int gsm48_rr_data_req(struct osmocom_ms *ms, struct msgb *msg)
{
@@ -5144,6 +5429,8 @@
switch (sgh->msg_type) {
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;
@@ -5175,6 +5462,8 @@
}
gh = msgb_l3(msg);
switch (gh->msg_type) {
+ 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;
@@ -5233,6 +5522,10 @@
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);
@@ -5372,7 +5665,11 @@
stop_rr_t3110(rr);
/* send release indication */
- nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
+ if (rr->vgcs.group_state == GSM48_RR_GST_RECEIVE) {
+ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_GROUP_REL_IND);
+ rr->vgcs.group_state = GSM48_RR_GST_OFF;
+ } else
+ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
if (!nmsg)
return -ENOMEM;
nrrh = (struct gsm48_rr_hdr *)nmsg->data;
@@ -5839,6 +6136,14 @@
{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},
+
};
#define RRDOWNSLLEN \
@@ -6109,6 +6414,9 @@
struct gsm48_rrlayer *rr = &ms->rrlayer;
uint8_t ch_type, ch_subch, ch_ts;
+ if (ms->settings.audio.io_handler != AUDIO_IOH_NONE)
+ return 0;
+
LOGP(DRR, LOGL_INFO, "setting audio mode to %d\n", mode);
rr->audio_mode = mode;
To view, visit change 34531. To unsubscribe, or for help writing mail filters, visit settings.