lists.osmocom.org
Sign In Sign Up
Manage this list Sign In Sign Up

Keyboard Shortcuts

Thread View

  • j: Next unread message
  • k: Previous unread message
  • j a: Jump to all threads
  • j l: Jump to MailingList overview

gerrit-log

Thread Start a new thread
Download
Threads by month
  • ----- 2026 -----
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2025 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2024 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2023 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2022 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
gerrit-log@lists.osmocom.org

October 2023

  • 1 participants
  • 1843 discussions
[XL] Change in osmo-bts[master]: meas: also match stderr logging for meas_test
by laforge 03 Oct '23

03 Oct '23
Attention is currently required from: fixeria. laforge has posted comments on this change. ( https://gerrit.osmocom.org/c/osmo-bts/+/34597?usp=email ) Change subject: meas: also match stderr logging for meas_test ...................................................................... Patch Set 2: Code-Review+1 (1 comment) Patchset: PS2: not entirely sure if we really want to match 1:1 each character in hundreds of lines of measurement logging output. But' let's see if and how often it breaks... -- To view, visit https://gerrit.osmocom.org/c/osmo-bts/+/34597?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmo-bts Gerrit-Branch: master Gerrit-Change-Id: Id1ce154a65b194ce5fd9257c9c1b26d62aad1e0d Gerrit-Change-Number: 34597 Gerrit-PatchSet: 2 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-Attention: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Comment-Date: Tue, 03 Oct 2023 12:50:54 +0000 Gerrit-HasComments: Yes Gerrit-Has-Labels: Yes Gerrit-MessageType: comment
1 0
0 0
[S] Change in osmo-bts[master]: osmo-bts-trx: rx_tchh_fn(): fix copy-pasted comment
by laforge 03 Oct '23

03 Oct '23
Attention is currently required from: fixeria. laforge has posted comments on this change. ( https://gerrit.osmocom.org/c/osmo-bts/+/34591?usp=email ) Change subject: osmo-bts-trx: rx_tchh_fn(): fix copy-pasted comment ...................................................................... Patch Set 1: Code-Review+2 -- To view, visit https://gerrit.osmocom.org/c/osmo-bts/+/34591?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmo-bts Gerrit-Branch: master Gerrit-Change-Id: Idba18c217187f7aacc2fcb0c330814f3802a9306 Gerrit-Change-Number: 34591 Gerrit-PatchSet: 1 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-Attention: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Comment-Date: Tue, 03 Oct 2023 12:49:36 +0000 Gerrit-HasComments: No Gerrit-Has-Labels: Yes Gerrit-MessageType: comment
1 0
0 0
[S] Change in pysim[master]: Add swig to Debian installation instructions
by laforge 03 Oct '23

03 Oct '23
Attention is currently required from: Sebastian Schmidt. laforge has posted comments on this change. ( https://gerrit.osmocom.org/c/pysim/+/34600?usp=email ) Change subject: Add swig to Debian installation instructions ...................................................................... Patch Set 1: Code-Review-1 (1 comment) Patchset: PS1: I agree. It is not our job to list dependencies of other packages (which may or may not change over time anyway). If one of our dependencies misses its upstream dependency, the problem is there. -- To view, visit https://gerrit.osmocom.org/c/pysim/+/34600?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: pysim Gerrit-Branch: master Gerrit-Change-Id: I989dd8ccf8d089c784d566fe0359b85df7575fc3 Gerrit-Change-Number: 34600 Gerrit-PatchSet: 1 Gerrit-Owner: Sebastian Schmidt <yath+osmocom(a)yath.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Attention: Sebastian Schmidt <yath+osmocom(a)yath.de> Gerrit-Comment-Date: Tue, 03 Oct 2023 12:49:17 +0000 Gerrit-HasComments: Yes Gerrit-Has-Labels: Yes Gerrit-MessageType: comment
1 0
0 0
[S] Change in pysim[master]: Add pyshark to requirements.
by laforge 03 Oct '23

03 Oct '23
Attention is currently required from: Sebastian Schmidt. laforge has posted comments on this change. ( https://gerrit.osmocom.org/c/pysim/+/34601?usp=email ) Change subject: Add pyshark to requirements. ...................................................................... Patch Set 1: Code-Review-1 (1 comment) Patchset: PS1: as @dexter stated, we prefer to not have a hard dependency. most pysim users will likely never use pySim-trace, and hence never need that dependency. -- To view, visit https://gerrit.osmocom.org/c/pysim/+/34601?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: pysim Gerrit-Branch: master Gerrit-Change-Id: Ic93e83195feb7ada3b364bae86442dae90d81d90 Gerrit-Change-Number: 34601 Gerrit-PatchSet: 1 Gerrit-Owner: Sebastian Schmidt <yath+osmocom(a)yath.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-CC: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Attention: Sebastian Schmidt <yath+osmocom(a)yath.de> Gerrit-Comment-Date: Tue, 03 Oct 2023 12:48:09 +0000 Gerrit-HasComments: Yes Gerrit-Has-Labels: Yes Gerrit-MessageType: comment
1 0
0 0
[L] Change in osmocom-bb[master]: ASCI: Add group receive mode support to RR layer
by laforge 03 Oct '23

03 Oct '23
laforge has submitted this change. ( https://gerrit.osmocom.org/c/osmocom-bb/+/34531?usp=email ) Change subject: ASCI: Add group receive mode support to RR layer ...................................................................... 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(-) Approvals: fixeria: Looks good to me, approved Jenkins Builder: Verified pespin: Looks good to me, but someone else must approve 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 https://gerrit.osmocom.org/c/osmocom-bb/+/34531?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmocom-bb Gerrit-Branch: master Gerrit-Change-Id: Ia7d806b354fb3be5729bff8ac9aa1c7ad7a8b539 Gerrit-Change-Number: 34531 Gerrit-PatchSet: 7 Gerrit-Owner: jolly <andreas(a)eversberg.eu> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-MessageType: merged
1 0
0 0
[L] Change in osmocom-bb[master]: ASCI: Add group receive and transmit mode support to MM layer
by laforge 03 Oct '23

03 Oct '23
laforge has submitted this change. ( https://gerrit.osmocom.org/c/osmocom-bb/+/34492?usp=email ) ( 14 is the latest approved patch-set. No files were changed between the latest approved patch-set and the submitted one. )Change subject: ASCI: Add group receive and transmit mode support to MM layer ...................................................................... ASCI: Add group receive and transmit mode support to MM layer Related: OS#5364 Change-Id: I05957182a57423ad947ab200b52f65fde859e110 --- M src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h M src/host/layer23/src/mobile/gsm48_mm.c 2 files changed, 710 insertions(+), 78 deletions(-) Approvals: pespin: Looks good to me, but someone else must approve Jenkins Builder: Verified fixeria: Looks good to me, approved diff --git a/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h b/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h index 67abc2f..1f0db78 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h +++ b/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h @@ -8,8 +8,8 @@ #define GSM48_MMCC_CLASS 0x100 #define GSM48_MMSS_CLASS 0x200 #define GSM48_MMSMS_CLASS 0x300 -#define GSM48_MMBCC_CLASS 0x500 -#define GSM48_MMGCC_CLASS 0x600 +#define GSM48_MMGCC_CLASS 0x500 +#define GSM48_MMBCC_CLASS 0x600 #define GSM48_MMXX_REL_IND 0x022 #define GSM48_MMCC_EST_REQ 0x110 #define GSM48_MMCC_EST_IND 0x112 @@ -66,6 +66,15 @@ #define GSM48_MMGCC_REEST_REQ 0x560 #define GSM48_MMGCC_REEST_CNF 0x561 #define GSM48_MMGCC_ERR_IND 0x572 +#define GSM48_MMGCC_NOTIF_IND 0x582 +#define GSM48_MMGCC_GROUP_REQ 0x590 +#define GSM48_MMGCC_GROUP_CNF 0x591 +#define GSM48_MMGCC_UPLINK_REQ 0x5a0 +#define GSM48_MMGCC_UPLINK_CNF 0x5a1 +#define GSM48_MMGCC_UPLINK_REL_REQ 0x5a8 +#define GSM48_MMGCC_UPLINK_REL_IND 0x5aa +#define GSM48_MMGCC_UPLINK_FREE_IND 0x5b2 +#define GSM48_MMGCC_UPLINK_BUSY_IND 0x5b6 #define GSM48_MMBCC_EST_REQ 0x610 #define GSM48_MMBCC_EST_CNF 0x611 #define GSM48_MMBCC_REL_REQ 0x620 @@ -77,18 +86,34 @@ #define GSM48_MMBCC_REEST_REQ 0x660 #define GSM48_MMBCC_REEST_CNF 0x661 #define GSM48_MMBCC_ERR_IND 0x672 +#define GSM48_MMBCC_NOTIF_IND 0x682 +#define GSM48_MMBCC_GROUP_REQ 0x690 +#define GSM48_MMBCC_GROUP_CNF 0x691 +#define GSM48_MMBCC_UPLINK_REQ 0x6a0 +#define GSM48_MMBCC_UPLINK_CNF 0x6a1 +#define GSM48_MMBCC_UPLINK_REL_REQ 0x6a8 +#define GSM48_MMBCC_UPLINK_REL_IND 0x6aa +#define GSM48_MMBCC_UPLINK_FREE_IND 0x6b2 +#define GSM48_MMBCC_UPLINK_BUSY_IND 0x6b6 + #define MMXX_ALLOC_SIZE 256 #define MMXX_ALLOC_HEADROOM 64 +#define MMXX_NOTIFY_SETUP 0 +#define MMXX_NOTIFY_RELEASE 1 + /* MMxx-SAP header */ struct gsm48_mmxx_hdr { - uint16_t msg_type; /* MMxx_* primitive */ - uint32_t ref; /* reference to transaction */ - uint32_t transaction_id; /* transaction identifier */ - uint8_t sapi; /* sapi */ - uint8_t emergency; /* emergency type of call */ - uint8_t cause; /* cause used for release */ + uint16_t msg_type; /* MMxx_* primitive */ + uint32_t ref; /* reference to transaction */ + uint32_t transaction_id; /* transaction identifier */ + uint8_t sapi; /* sapi */ + uint8_t emergency; /* emergency type of call */ + uint8_t cause; /* cause used for release */ + uint8_t notify; /* notify ongoing ASCI call */ + bool ch_desc_present; /* notifies channel */ + struct gsm48_chan_desc ch_desc; /* group channel */ } __attribute__((packed)); /* GSM 6.1.2 */ @@ -237,6 +262,14 @@ /* sapi 3 */ int sapi3_link; + + /* VGCS additional states */ + struct { + bool enabled; /* We are in group/broadcast mode. */ + bool group_call; /* This is a group call, not a broadcast call. */ + uint32_t callref; /* Callref of this call. */ + bool normal_service; /* Service state before group transmit mode. */ + } vgcs; }; /* MM connection entry */ diff --git a/src/host/layer23/src/mobile/gsm48_mm.c b/src/host/layer23/src/mobile/gsm48_mm.c index 8c57785..82e5fad 100644 --- a/src/host/layer23/src/mobile/gsm48_mm.c +++ b/src/host/layer23/src/mobile/gsm48_mm.c @@ -60,6 +60,7 @@ static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg); static int gsm48_mm_loc_upd_periodic(struct osmocom_ms *ms, struct msgb *msg); static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_mm_group_rel_req(struct osmocom_ms *ms, struct msgb *msg); /* * notes @@ -144,6 +145,9 @@ * During LIMITED SERVICE state: (4.2.2.3) * - reject MM connection except for emergency calls * - perform location update, if new LAI is entered + * - indicate GCC/BCC calls with channel description only + * - reject joining to GCC/BCC calls without channel description + * - accept joining to GCC/BCC calls with channel description * * * The LOCATION UPDATE NEEDED state is entered if: @@ -176,6 +180,9 @@ * - accept MM connection for emergency calls * - trigger location update on any other MM connection * - respond to paging (with IMSI only, because in U2 TMSI is not valid) + * - indicate GCC/BCC calls with channel description only + * - reject joining to GCC/BCC calls without channel description + * - accept joining to GCC/BCC calls with channel description * * * The NORMAL SERVICE state is entered if: @@ -185,12 +192,52 @@ * - and SIM LAI == cell * * During NORMAL SERVICE state: (4.2.2.1) - * - on expirery of T3211 or T3213: Perform location updated - * - on expirery of T3212: Perform location updated + * - on expirery of T3211 or T3213: Perform location update + * - on expirery of T3212: Perform location update * - on change of LAI: Perform location update * - perform IMSI detach * - perform MM connections * - respond to paging + * - indicate GCC/BCC calls with and without channel description + * - accept joining to GCC/BCC calls without channel description + * -> The GCC/BCC layer waits for channel description before joining. + * - accept joining to GCC/BCC calls with channel description + * + * + * The RECEIVING GROUP CALL (NORMAL SERVICE) is entered if: + * - the upper layer requests to join to GCC/BCC call + * - and service state is NORMAL SERVICE + * + * During RECEIVING GROUP CALL (NORMAL SERVICE) state: (4.2.2.7) + * - reject all MM connections + * - indicate notifications and paging to GCC or BCC layer + * -> If supported by RR layer. + * The following events are not be supported here: + * - perform IMSI detach (This is delayed until the call has ended.) + * - on expirery of T3211 or T3213: Perform location update + * - on expirery of T3212: Perform location update + * - accept MM connections + * - on change of LAI: Perform location update + * - accept joining to GCC/BCC calls without channel description + * - accept joining to GCC/BCC calls with channel description + * + * + * The RECEIVING GROUP CALL (LIMITED SERVICE) is entered if: + * - the upper layer requests to join to GCC/BCC call + * - and service state is LIMITED SERVICE or ATTEMPTING TO UPDATE + * + * During RECEIVING GROUP CALL (LIMITED SERVICE) state: (4.2.2.8) + * - reject all MM connections + * - indicate notifications and paging to GCC or BCC layer + * -> If supported by RR layer. + * The following events are not be supported here: + * - reject MM connection except for emergency calls + * - on expirery of T3212: Perform location updated + * - reject joining to GCC/BCC calls without channel description + * - accept joining to GCC/BCC calls with channel description + * + * + * A group call is only accepted, if there is no other MM connection ongoing. * * * gsm48_mm_set_plmn_search() is used enter PLMN SEARCH or PLMN SEARCH NORMAL @@ -223,6 +270,33 @@ * * gsm48_mm_cell_selected() is used to select the Service state. * + * + * New primitives are invented for group/broadcast calls. They are not + * specified in any recommendation. They are: + * + * - MMxCC_NOTIF_IND: The MM layer indicates new/updated/ceased calls. This is + * completely independent from the state of the MM layer. + * - MMxCC_GROUP_REQ: The GCC/BCC layer requests group channel in receive mode. + * This mode has no MM connection. Speical flags (mm->vgcs*) are used to + * define that mode and store the reference (callref + group|broadcast). This + * reference is used for messages towards GCC/BCC layer. The state is IDLE + * and the sub-state defines group receive mode at normal service or at + * limited service state. + * - MMxCC_GROUP_CNF: The MM layer confirms group channel. + * - MMxCC_UPLINK_REQ: The GCC/BCC layer requests uplink (group transmit mode). + * The MM layer changes to group transmit mode. + * - MMxCC_UPLINK_CNF: The MM layer confirms uplink. (Uplink was granted.) + * - MMxCC_UPLINK_REL_REQ: The GCC/BCC layer requests release of uplink. + * - MMxCC_UPLINK_REL_IND: The MM layer indicates/confirms release of uplink + * + * Existing primitives are used with group calls: + * + * MMxCC_REL_IND: Failed to establish group receive mode. + * MMxCC_ERR_IND: Abort received from RR layer in group receive or transmit mode. + * MMxCC_REL_REQ: Leave group call (receive mode or transmit mode). + * + * The group call is released at MM layer, if one of the primitives above are + * received or transmitted. */ /* @@ -578,6 +652,8 @@ { GSM48_MM_EVENT_USER_PLMN_SEL, "MM_EVENT_USER_PLMN_SEL" }, { GSM48_MM_EVENT_LOST_COVERAGE, "MM_EVENT_LOST_COVERAGE" }, { GSM48_MM_EVENT_NOTIFICATION, "MM_EVENT_NOTIFICATION" }, + { GSM48_MM_EVENT_UPLINK_FREE, "MM_EVENT_UPLINK_FREE" }, + { GSM48_MM_EVENT_UPLINK_BUSY, "MM_EVENT_UPLINK_BUSY" }, { 0, NULL } }; @@ -662,6 +738,26 @@ { GSM48_MMSMS_ERR_IND, "MMSMS_ERR_IND" }, { GSM48_MMSMS_PROMPT_IND, "MMSMS_PROMPT_IND" }, { GSM48_MMSMS_PROMPT_REJ, "MMSMS_PROMPT_REJ" }, + { GSM48_MMGCC_EST_REQ, "MMGCC_EST_REQ" }, + { GSM48_MMGCC_EST_CNF, "MMGCC_EST_CNF" }, + { GSM48_MMGCC_REL_REQ, "MMGCC_REL_REQ" }, + { GSM48_MMGCC_REL_IND, "MMGCC_REL_IND" }, + { GSM48_MMGCC_DATA_REQ, "MMGCC_DATA_REQ" }, + { GSM48_MMGCC_DATA_IND, "MMGCC_DATA_IND" }, + { GSM48_MMGCC_UNIT_DATA_REQ, "MMGCC_UNIT_DATA_REQ" }, + { GSM48_MMGCC_UNIT_DATA_IND, "MMGCC_UNIT_DATA_IND" }, + { GSM48_MMGCC_REEST_REQ, "MMBCC_REEST_REQ" }, + { GSM48_MMGCC_REEST_CNF, "MMBCC_REEST_CNF" }, + { GSM48_MMGCC_ERR_IND, "MMGCC_ERR_IND" }, + { GSM48_MMGCC_NOTIF_IND, "MMGCC_NOTIF_IND" }, + { GSM48_MMGCC_GROUP_REQ, "MMGCC_GROUP_REQ" }, + { GSM48_MMGCC_GROUP_CNF, "MMGCC_GROUP_CNF" }, + { GSM48_MMGCC_UPLINK_REQ, "MMGCC_UPLINK_REQ" }, + { GSM48_MMGCC_UPLINK_CNF, "MMGCC_UPLINK_CNF" }, + { GSM48_MMGCC_UPLINK_REL_REQ, "MMGCC_UPLINK_REL_REQ" }, + { GSM48_MMGCC_UPLINK_REL_IND, "MMGCC_UPLINK_REL_CNF" }, + { GSM48_MMGCC_UPLINK_FREE_IND, "MMGCC_UPLINK_FREE_IND" }, + { GSM48_MMGCC_UPLINK_BUSY_IND, "MMGCC_UPLINK_BUSY_IND" }, { GSM48_MMBCC_EST_REQ, "MMBCC_EST_REQ" }, { GSM48_MMBCC_EST_CNF, "MMBCC_EST_CNF" }, { GSM48_MMBCC_REL_REQ, "MMBCC_REL_REQ" }, @@ -673,17 +769,15 @@ { GSM48_MMBCC_REEST_REQ, "MMBCC_REEST_REQ" }, { GSM48_MMBCC_REEST_CNF, "MMBCC_REEST_CNF" }, { GSM48_MMBCC_ERR_IND, "MMBCC_ERR_IND" }, - { GSM48_MMGCC_EST_REQ, "MMGCC_EST_REQ" }, - { GSM48_MMGCC_EST_CNF, "MMGCC_EST_CNF" }, - { GSM48_MMGCC_REL_REQ, "MMGCC_REL_REQ" }, - { GSM48_MMGCC_REL_IND, "MMGCC_REL_IND" }, - { GSM48_MMGCC_DATA_REQ, "MMGCC_DATA_REQ" }, - { GSM48_MMGCC_DATA_IND, "MMGCC_DATA_IND" }, - { GSM48_MMGCC_UNIT_DATA_REQ, "MMGCC_UNIT_DATA_REQ" }, - { GSM48_MMGCC_UNIT_DATA_IND, "MMGCC_UNIT_DATA_IND" }, - { GSM48_MMGCC_REEST_REQ, "MMGCC_REEST_REQ" }, - { GSM48_MMGCC_REEST_CNF, "MMGCC_REEST_CNF" }, - { GSM48_MMGCC_ERR_IND, "MMGCC_ERR_IND" }, + { GSM48_MMBCC_NOTIF_IND, "MMBCC_NOTIF_IND" }, + { GSM48_MMBCC_GROUP_REQ, "MMBCC_GROUP_REQ" }, + { GSM48_MMBCC_GROUP_CNF, "MMBCC_GROUP_CNF" }, + { GSM48_MMBCC_UPLINK_REQ, "MMBCC_UPLINK_REQ" }, + { GSM48_MMBCC_UPLINK_CNF, "MMBCC_UPLINK_CNF" }, + { GSM48_MMBCC_UPLINK_REL_REQ, "MMBCC_UPLINK_REL_REQ" }, + { GSM48_MMBCC_UPLINK_REL_IND, "MMBCC_UPLINK_REL_CNF" }, + { GSM48_MMBCC_UPLINK_FREE_IND, "MMBCC_UPLINK_FREE_IND" }, + { GSM48_MMBCC_UPLINK_BUSY_IND, "MMBCC_UPLINK_BUSY_IND" }, { 0, NULL } }; @@ -913,8 +1007,8 @@ "wait for RR connection active", "MM idle", "wait for additional outgoing MM connection", - "MM_CONN_ACTIVE_VGCS", - "WAIT_RR_CONN_VGCS", + "MM connection active (group transmit mode)", + "wait for RR connection (group transmit mode)", "location updating pending", "IMSI detach pending", "RR connection release not allowed" @@ -930,8 +1024,8 @@ "location updating needed", "PLMN search", "PLMN search (normal)", - "RX_VGCS_NORMAL", - "RX_VGCS_LIMITED" + "receiving group call (normal service)", + "receiving group call (limiteed service)" }; /* change state from LOCATION UPDATE NEEDED to ATTEMPTING TO UPDATE */ @@ -1003,6 +1097,11 @@ l23_vty_ms_notify(ms, "Trying to register with network %s, %s...\n", gsm_get_mcc(plmn->plmn.mcc), gsm_get_mnc(&plmn->plmn)); break; + case GSM48_MM_SST_RX_VGCS_NORMAL: + case GSM48_MM_SST_RX_VGCS_LIMITED: + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Listening to %s call.\n", (mm->vgcs.group_call) ? "group" : "broadcast"); + break; } } @@ -1126,6 +1225,7 @@ struct gsm48_mmlayer *mm = &ms->mmlayer; struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = &cs->sel_si; + bool vgcs = mm->vgcs.enabled; if (cs->state != GSM322_C3_CAMPED_NORMALLY && cs->state != GSM322_C7_CAMPED_ANY_CELL) { @@ -1176,7 +1276,8 @@ GSM48_MM_SST_ATTEMPT_UPDATE); else new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_NORMAL_SERVICE); + (vgcs) ? GSM48_MM_SST_RX_VGCS_NORMAL + : GSM48_MM_SST_NORMAL_SERVICE); return 0; } @@ -1188,13 +1289,15 @@ /* location update not allowed */ LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed PLMN.\n"); new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_LIMITED_SERVICE); + (vgcs) ? GSM48_MM_SST_RX_VGCS_LIMITED + : GSM48_MM_SST_LIMITED_SERVICE); } else if (gsm322_is_forbidden_la(ms, &cs->sel_cgi.lai)) { /* location update not allowed */ LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed LA.\n"); new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_LIMITED_SERVICE); + (vgcs) ? GSM48_MM_SST_RX_VGCS_LIMITED + : GSM48_MM_SST_LIMITED_SERVICE); } else /* 4.4.4.9 if cell is barred, don't start */ if ((!subscr->acc_barr && s->cell_barr) @@ -1202,19 +1305,22 @@ (s->class_barr ^ 0xffff)))) { LOGP(DMM, LOGL_INFO, "Loc. upd. no access.\n"); new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_LIMITED_SERVICE); + (vgcs) ? GSM48_MM_SST_RX_VGCS_LIMITED + : GSM48_MM_SST_LIMITED_SERVICE); } else { /* location update allowed */ LOGP(DMM, LOGL_INFO, "Loc. upd. allowed.\n"); new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_LOC_UPD_NEEDED); + (vgcs) ? GSM48_MM_SST_RX_VGCS_LIMITED + : GSM48_MM_SST_LIMITED_SERVICE); } } else { /* location update not allowed */ LOGP(DMM, LOGL_INFO, "We are camping on any cell as returning " "to MM IDLE\n"); new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_LIMITED_SERVICE); + (vgcs) ? GSM48_MM_SST_RX_VGCS_LIMITED + : GSM48_MM_SST_LIMITED_SERVICE); } return 0; @@ -1241,6 +1347,7 @@ struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = &cs->sel_si; struct gsm_settings *set = &ms->settings; + bool vgcs = mm->vgcs.enabled; /* no SIM is inserted */ if (!subscr->sim_valid) { @@ -1260,7 +1367,8 @@ LOGP(DMM, LOGL_INFO, "Valid in location area.\n"); new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_NORMAL_SERVICE); + (vgcs) ? GSM48_MM_SST_RX_VGCS_NORMAL + : GSM48_MM_SST_NORMAL_SERVICE); /* send message to PLMN search process */ nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS); @@ -1275,7 +1383,8 @@ LOGP(DMM, LOGL_INFO, "Attachment not required.\n"); new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_NORMAL_SERVICE); + (vgcs) ? GSM48_MM_SST_RX_VGCS_NORMAL + : GSM48_MM_SST_NORMAL_SERVICE); /* send message to PLMN search process */ nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS); @@ -1297,7 +1406,8 @@ LOGP(DMM, LOGL_INFO, "Selected cell is forbidden.\n"); new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_LIMITED_SERVICE); + (vgcs) ? GSM48_MM_SST_RX_VGCS_LIMITED + : GSM48_MM_SST_LIMITED_SERVICE); /* send message to PLMN search process */ nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_FAILED); @@ -1318,7 +1428,8 @@ LOGP(DMM, LOGL_INFO, "Selected cell not found.\n"); new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_LIMITED_SERVICE); + (vgcs) ? GSM48_MM_SST_RX_VGCS_LIMITED + : GSM48_MM_SST_LIMITED_SERVICE); /* send message to PLMN search process */ nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_FAILED); @@ -2029,6 +2140,32 @@ return gsm48_mm_imsi_detach_sent(ms, msg); } +/* Detach during VGCS. Queue and return idle. */ +static int gsm48_mm_imsi_detach_vgcs(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + int msg_type; + + /* remember to detach later */ + mm->delay_detach = 1; + + /* Release group call. */ + gsm48_mm_group_rel_req(ms, msg); + + /* Release message to GCC/BCC layer */ + msg_type = (mm->vgcs.group_call) ? GSM48_MMGCC_REL_IND : GSM48_MMBCC_REL_IND; + nmsg = gsm48_mmxx_msgb_alloc(msg_type, mm->vgcs.callref, 0xff, 0); + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + nmmh->cause = GSM48_CC_CAUSE_NORMAL_UNSPEC; + + gsm48_mmxx_upmsg(ms, nmsg); + return 0; +} + /* ignore ongoing IMSI detach */ static int gsm48_mm_imsi_detach_ignore(struct osmocom_ms *ms, struct msgb *msg) { @@ -2145,6 +2282,8 @@ if (mm->state == GSM48_MM_ST_MM_IDLE && (mm->substate == GSM48_MM_SST_NO_CELL_AVAIL || mm->substate == GSM48_MM_SST_LIMITED_SERVICE + || mm->substate == GSM48_MM_SST_RX_VGCS_NORMAL + || mm->substate == GSM48_MM_SST_RX_VGCS_LIMITED || mm->substate == GSM48_MM_SST_PLMN_SEARCH || mm->substate == GSM48_MM_SST_PLMN_SEARCH_NORMAL)) return 0; @@ -2276,6 +2415,7 @@ struct gsm_subscriber *subscr = &ms->subscr; struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = &cs->sel_si; + bool vgcs = mm->vgcs.enabled; struct msgb *nmsg; /* in case we already have a location update going on */ @@ -2315,7 +2455,8 @@ /* go straight to normal service state */ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_NORMAL_SERVICE); + (vgcs) ? GSM48_MM_SST_RX_VGCS_NORMAL + : GSM48_MM_SST_NORMAL_SERVICE); #if 0 /* don't send message, if we got not triggered by PLMN search */ @@ -3397,6 +3538,7 @@ { struct gsm48_mmlayer *mm = &ms->mmlayer; struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data; + uint32_t msg_type = rrh->msg_type; int cause; /* stop RR release timer */ @@ -3416,11 +3558,13 @@ cause = 47; } + LOGP(DMM, LOGL_INFO, "Aborting connection with cause %d\n", cause); + /* stop MM connection timer */ stop_mm_t3230(mm); /* release all connections */ - gsm48_mm_release_mm_conn(ms, 1, cause, 1, 0); + gsm48_mm_release_mm_conn(ms, 1, cause, (msg_type == GSM48_RR_ABORT_IND), 0); /* return to MM IDLE */ return gsm48_mm_return_idle(ms, NULL); @@ -3464,27 +3608,32 @@ struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; struct gsm48_mm_conn *conn; int msg_type = mmh->msg_type; + uint8_t sapi; - /* get connection, if not exist (anymore), release */ - conn = mm_conn_by_ref_and_class(mm, mmh->ref, (mmh->msg_type & GSM48_MMXX_MASK)); - if (!conn) { - LOGP(DMM, LOGL_INFO, "MMXX_DATA_REQ with unknown (already " - "released) ref=%x, sending MMXX_REL_IND\n", mmh->ref); - mmh->msg_type = GSM48_MMXX_REL_IND | (msg_type & GSM48_MMXX_MASK); - mmh->cause = 31; + if (mm->state == GSM48_MM_ST_MM_CONN_ACTIVE_VGCS) { + /* Group transmit mode has no MM connection. */ + sapi = 0; + } else { + /* get connection, if not exist (anymore), release */ + conn = mm_conn_by_ref_and_class(mm, mmh->ref, (mmh->msg_type & GSM48_MMXX_MASK)); + if (!conn) { + LOGP(DMM, LOGL_INFO, "MMXX_DATA_REQ with unknown (already " + "released) ref=%x, sending MMXX_REL_IND\n", mmh->ref); + mmh->msg_type = GSM48_MMXX_REL_IND | (msg_type & GSM48_MMXX_MASK); + mmh->cause = 31; - /* mirror message with REL_IND + cause */ - return gsm48_mmxx_upmsg(ms, msg); + /* mirror message with REL_IND + cause */ + return gsm48_mmxx_upmsg(ms, msg); + } + /* set SAPI, if upper layer does not do it correctly */ + sapi = conn->sapi; } - /* set SAPI, if upper layer does not do it correctly */ - mmh->sapi = conn->sapi; - /* pull MM header */ msgb_pull(msg, sizeof(struct gsm48_mmxx_hdr)); /* push RR header and send down */ - return gsm48_mm_to_rr(ms, msg, GSM48_RR_DATA_REQ, conn->sapi, 0); + return gsm48_mm_to_rr(ms, msg, GSM48_RR_DATA_REQ, sapi, 0); } /* release of MM connection (active state) */ @@ -3591,6 +3740,336 @@ return gsm48_mm_return_idle(ms, NULL); } +/* The RR indicates notification of ongoing VGCS/VBS calls. */ +static int gsm48_mm_notification(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mm_event *mme = (struct gsm48_mm_event *)msg->data; + uint32_t ref = osmo_load32be(mme->notification.gcr) >> 5; + uint16_t msg_type = (mme->notification.gcr[3] & 0x10) ? GSM48_MMGCC_NOTIF_IND : GSM48_MMBCC_NOTIF_IND; + struct gsm48_mmxx_hdr *mmh; + struct msgb *nmsg; + + /* Notification message to GCC/BCC layer */ + nmsg = gsm48_mmxx_msgb_alloc(msg_type, ref, 0xff, 0); + if (!nmsg) + return -ENOMEM; + mmh = (struct gsm48_mmxx_hdr *)nmsg->data; + mmh->notify = (mme->notification.gone) ? MMXX_NOTIFY_RELEASE : MMXX_NOTIFY_SETUP; + mmh->ch_desc_present = mme->notification.ch_desc_present; + memcpy(&mmh->ch_desc, &mme->notification.ch_desc, sizeof(mmh->ch_desc)); + + gsm48_mmxx_upmsg(ms, nmsg); + return 0; +} + +/* The RR indicates uplink busy. */ +static int gsm48_mm_uplink_free(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mm_event *mme = (struct gsm48_mm_event *)msg->data; + struct msgb *nmsg; + uint16_t msg_type; + + if (mm->vgcs.group_call) + msg_type = (mme->msg_type == GSM48_MM_EVENT_UPLINK_BUSY) ? GSM48_MMGCC_UPLINK_BUSY_IND + : GSM48_MMGCC_UPLINK_FREE_IND; + else + msg_type = (mme->msg_type == GSM48_MM_EVENT_UPLINK_BUSY) ? GSM48_MMBCC_UPLINK_BUSY_IND + : GSM48_MMBCC_UPLINK_FREE_IND; + + LOGP(DMM, LOGL_INFO, "Update uplink free/busy state in group receive mode.\n"); + + /* Notification message to GCC/BCC layer */ + nmsg = gsm48_mmxx_msgb_alloc(msg_type, mm->vgcs.callref, 0xff, 0); + if (!nmsg) + return -ENOMEM; + + gsm48_mmxx_upmsg(ms, nmsg); + return 0; +} + +/* Join VGCS/VBS call as listener. */ +static int gsm48_mm_group_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + struct msgb *nmsg; + + LOGP(DMM, LOGL_INFO, "Request for joining a group call, trying to establish group receive mode.\n"); + + /* Store infos about group/broadcast call. */ + mm->vgcs.enabled = true; + mm->vgcs.group_call = (mmh->msg_type == GSM48_MMGCC_GROUP_REQ); + mm->vgcs.callref = mmh->ref; + mm->vgcs.normal_service = (mm->substate == GSM48_MM_SST_NORMAL_SERVICE); + + /* Change to VGCS substate. */ + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, (mm->substate == GSM48_MM_SST_NORMAL_SERVICE) + ? GSM48_MM_SST_RX_VGCS_NORMAL : GSM48_MM_SST_RX_VGCS_LIMITED); + + /* Group recevie mode request to RR layer */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + + /* Add channel description. */ + memcpy(msgb_put(nmsg, sizeof(mmh->ch_desc)), &mmh->ch_desc, sizeof(mmh->ch_desc)); + + /* Push RR header and send to RR layer. */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_GROUP_REQ, 0, 0); +} + +/* Joining VGCS/VBS call is not allowed in other states. */ +static int gsm48_mm_group_reject(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + uint16_t msg_type; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + + LOGP(DMM, LOGL_NOTICE, "Joining group call rejected in current state.\n"); + + msg_type = (mmh->msg_type == GSM48_MMGCC_GROUP_REQ) ? GSM48_MMGCC_REL_IND : GSM48_MMBCC_REL_IND; + + /* Release message to GCC/BCC layer */ + nmsg = gsm48_mmxx_msgb_alloc(msg_type, mmh->ref, mmh->transaction_id, mmh->sapi); + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + nmmh->cause = GSM48_CC_CAUSE_CALL_REJECTED; + + gsm48_mmxx_upmsg(ms, nmsg); + return 0; +} + +/* RR layer confirms group call. */ +static int gsm48_mm_group_cnf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + uint16_t msg_type; + struct msgb *nmsg; + + LOGP(DMM, LOGL_NOTICE, "RR confirms group call.\n"); + + msg_type = (mm->vgcs.group_call) ? GSM48_MMGCC_GROUP_CNF : GSM48_MMBCC_GROUP_CNF; + + /* Uplink confirm message to GCC/BCC layer */ + nmsg = gsm48_mmxx_msgb_alloc(msg_type, mm->vgcs.callref, 0xff, 0); + if (!nmsg) + return -ENOMEM; + + + gsm48_mmxx_upmsg(ms, nmsg); + return 0; +} + +/* RR layer releases group call channel. */ +static int gsm48_mm_group_rel_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data; + uint16_t msg_type; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + + LOGP(DMM, LOGL_NOTICE, "RR released or rejected group call channel.\n"); + + /* Disable group mode. */ + mm->vgcs.enabled = false; + + /* Change mode back to normal or limited service. */ + if (mm->substate == GSM48_MM_SST_RX_VGCS_LIMITED) + mm->substate = GSM48_MM_SST_LIMITED_SERVICE; + if (mm->substate == GSM48_MM_SST_RX_VGCS_NORMAL) + mm->substate = GSM48_MM_SST_NORMAL_SERVICE; + + /* Return IDLE, if not already. Also select the sub-state to use. */ + gsm48_mm_return_idle(ms, NULL); + + /* Release message to GCC/BCC layer */ + msg_type = (mm->vgcs.group_call) ? GSM48_MMGCC_REL_IND : GSM48_MMBCC_REL_IND; + nmsg = gsm48_mmxx_msgb_alloc(msg_type, mm->vgcs.callref, 0xff, 0); + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + switch (rrh->cause) { + case RR_REL_CAUSE_TRY_LATER: + /* Joining not yet possible */ + nmmh->cause = GSM48_CC_CAUSE_TEMP_FAILURE; + break; + case RR_REL_CAUSE_LOST_SIGNAL: + /* Lower layer failed. */ + nmmh->cause = GSM48_CC_CAUSE_TEMP_FAILURE; + break; + case RR_REL_CAUSE_NORMAL: + /* Channel was released by network. */ + nmmh->cause = GSM48_CC_CAUSE_NORM_CALL_CLEAR; + break; + default: + nmmh->cause = GSM48_CC_CAUSE_NORMAL_UNSPEC; + break; + } + + gsm48_mmxx_upmsg(ms, nmsg); + return 0; +} + +/* Upper layer releases group call. */ +static int gsm48_mm_group_rel_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct msgb *nmsg; + + LOGP(DMM, LOGL_INFO, "Request to release group call in receive or transmit mode.\n"); + + /* Disable group mode. */ + mm->vgcs.enabled = false; + + /* Change mode back to normal or limited service. */ + if (mm->substate == GSM48_MM_SST_RX_VGCS_LIMITED) + mm->substate = GSM48_MM_SST_LIMITED_SERVICE; + if (mm->substate == GSM48_MM_SST_RX_VGCS_NORMAL) + mm->substate = GSM48_MM_SST_NORMAL_SERVICE; + + /* We are already IDLE. Also select the sub-state to use. */ + gsm48_mm_return_idle(ms, NULL); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + + /* Push RR header and send to RR layer. */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_GROUP_REL_REQ, 0, 0); +} + +/* Upper layer requests uplink. */ +static int gsm48_mm_uplink_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct msgb *nmsg; + + LOGP(DMM, LOGL_INFO, "Request for uplink, trying to establish group transmit mode.\n"); + + /* Go into uplink pending state. */ + new_mm_state(mm, GSM48_MM_ST_WAIT_RR_CONN_VGCS, 0); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + + /* Push RR header and send to RR layer. */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_UPLINK_REQ, 0, 0); +} + +/* Uplink not allowed in this state. */ +static int gsm48_mm_uplink_reject(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + uint16_t msg_type; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + + LOGP(DMM, LOGL_NOTICE, "Request for uplink rejected in current state.\n"); + + msg_type = (mmh->msg_type == GSM48_MMGCC_UPLINK_REQ) ? GSM48_MMGCC_UPLINK_REL_IND : GSM48_MMBCC_UPLINK_REL_IND; + + /* Uplink release message to GCC/BCC layer */ + nmsg = gsm48_mmxx_msgb_alloc(msg_type, mmh->ref, mmh->transaction_id, mmh->sapi); + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + nmmh->cause = GSM48_CC_CAUSE_CALL_REJECTED; + + gsm48_mmxx_upmsg(ms, nmsg); + return 0; +} + +/* RR layer confirms uplink. */ +static int gsm48_mm_uplink_cnf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + uint16_t msg_type; + struct msgb *nmsg; + + LOGP(DMM, LOGL_NOTICE, "RR confirms uplink.\n"); + + /* Go into group transmit state. */ + new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE_VGCS, 0); + + msg_type = (mm->vgcs.group_call) ? GSM48_MMGCC_UPLINK_CNF : GSM48_MMBCC_UPLINK_CNF; + + /* Uplink confirm message to GCC/BCC layer */ + nmsg = gsm48_mmxx_msgb_alloc(msg_type, mm->vgcs.callref, 0xff, 0); + if (!nmsg) + return -ENOMEM; + + gsm48_mmxx_upmsg(ms, nmsg); + return 0; +} + +/* RR layer releases/rejects uplink. */ +static int gsm48_mm_uplink_rel_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data; + uint16_t msg_type; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + + LOGP(DMM, LOGL_NOTICE, "RR released or rejected uplink.\n"); + + /* Change to VGCS substate. */ + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, (mm->vgcs.normal_service) ? GSM48_MM_SST_RX_VGCS_NORMAL + : GSM48_MM_SST_RX_VGCS_LIMITED); + + msg_type = (mm->vgcs.group_call) ? GSM48_MMGCC_UPLINK_REL_IND : GSM48_MMBCC_UPLINK_REL_IND; + + /* Uplink reject message to GCC/BCC layer */ + nmsg = gsm48_mmxx_msgb_alloc(msg_type, mm->vgcs.callref, 0xff, 0); + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + switch (rrh->cause) { + case RR_REL_CAUSE_UPLINK_REJECTED: + /* Access to uplink was rejected by network or when not in group receive mode. */ + nmmh->cause = GSM48_CC_CAUSE_CALL_REJECTED; + break; + case RR_REL_CAUSE_UPLINK_BUSY: + /* Uplink was busy and did not become free. */ + nmmh->cause = GSM48_CC_CAUSE_USER_BUSY; + break; + case RR_REL_CAUSE_LINK_FAILURE: + /* Access to uplink failed. */ + nmmh->cause = GSM48_CC_CAUSE_TEMP_FAILURE; + break; + case RR_REL_CAUSE_NORMAL: + /* Uplink was released by message. */ + nmmh->cause = GSM48_CC_CAUSE_NORM_CALL_CLEAR; + break; + default: + nmmh->cause = GSM48_CC_CAUSE_NORMAL_UNSPEC; + break; + } + + gsm48_mmxx_upmsg(ms, nmsg); + return 0; +} + +/* Upper layer releases uplink. */ +static int gsm48_mm_uplink_rel_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct msgb *nmsg; + + LOGP(DMM, LOGL_INFO, "Request to release uplink, leaving group transmit mode.\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + + /* Push RR header and send to RR layer. */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_UPLINK_REL_REQ, 0, 0); +} + /* * other processes */ @@ -3675,15 +4154,33 @@ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), GSM48_MMBCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_req}, + /* 4.2.2.2 Attempt to update / Loc. Upd. needed */ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) | SBIT(GSM48_MM_SST_LOC_UPD_NEEDED), GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, /* emergency only */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE), + GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE), + GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_req}, + /* 4.2.2.3 Limited service */ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), + GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), + GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_req}, + /* 4.2.2.4 No IMSI */ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NO_IMSI), GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, @@ -3708,6 +4205,32 @@ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH), GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + /* 4.2.2.7 Receiving group call, normal service */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL), + GSM48_MMGCC_REL_REQ, gsm48_mm_group_rel_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL), + GSM48_MMBCC_REL_REQ, gsm48_mm_group_rel_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL), + GSM48_MMGCC_UPLINK_REQ, gsm48_mm_uplink_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL), + GSM48_MMBCC_UPLINK_REQ, gsm48_mm_uplink_req}, + + /* 4.2.2.8 Receiving group call, limited service */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_LIMITED), + GSM48_MMGCC_REL_REQ, gsm48_mm_group_rel_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_LIMITED), + GSM48_MMBCC_REL_REQ, gsm48_mm_group_rel_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_LIMITED), + GSM48_MMGCC_UPLINK_REQ, gsm48_mm_uplink_reject}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_LIMITED), + GSM48_MMBCC_UPLINK_REQ, gsm48_mm_uplink_reject}, + /* 4.5.1.1 MM Connection (EST) */ {SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES, GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_first}, @@ -3734,10 +4257,7 @@ GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_more}, {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES, - GSM48_MMGCC_EST_REQ, gsm48_mm_init_mm_more}, - - {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES, - GSM48_MMBCC_EST_REQ, gsm48_mm_init_mm_more}, + GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_req}, {SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES, GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_wait}, @@ -3748,12 +4268,7 @@ {SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES, GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_wait}, - {SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES, - GSM48_MMGCC_EST_REQ, gsm48_mm_init_mm_wait}, - - {SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES, - GSM48_MMBCC_EST_REQ, gsm48_mm_init_mm_wait}, - + /* Reject call in other states. */ {ALL_STATES, ALL_STATES, GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_reject}, @@ -3769,6 +4284,12 @@ {ALL_STATES, ALL_STATES, GSM48_MMBCC_EST_REQ, gsm48_mm_init_mm_reject}, + {ALL_STATES, ALL_STATES, + GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_reject}, + + {ALL_STATES, ALL_STATES, + GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_reject}, + /* 4.5.2.1 MM Connection (DATA) */ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) | SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, @@ -3844,6 +4365,29 @@ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), ALL_STATES, GSM48_MMBCC_REL_REQ, gsm48_mm_release_wait_rr}, + + /* Group transmit mode */ + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_VGCS) | + SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), ALL_STATES, + GSM48_MMGCC_UPLINK_REL_REQ, gsm48_mm_uplink_rel_req}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_VGCS) | + SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), ALL_STATES, + GSM48_MMBCC_UPLINK_REL_REQ, gsm48_mm_uplink_rel_req}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_VGCS) | + SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), ALL_STATES, + GSM48_MMGCC_REL_REQ, gsm48_mm_group_rel_req}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_VGCS) | + SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), ALL_STATES, + GSM48_MMBCC_REL_REQ, gsm48_mm_group_rel_req}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), ALL_STATES, + GSM48_MMGCC_DATA_REQ, gsm48_mm_data}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), ALL_STATES, + GSM48_MMBCC_DATA_REQ, gsm48_mm_data}, }; #define DOWNSLLEN \ @@ -3971,6 +4515,23 @@ SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), /* not supported */ GSM48_RR_ABORT_IND, gsm48_mm_abort_mm_con}, + /* Group call */ + {ALL_STATES, + GSM48_RR_GROUP_CNF, gsm48_mm_group_cnf}, + + {ALL_STATES, + GSM48_RR_UPLINK_CNF, gsm48_mm_uplink_cnf}, + + {ALL_STATES, + GSM48_RR_GROUP_REL_IND, gsm48_mm_group_rel_ind}, + + {ALL_STATES, + GSM48_RR_UPLINK_REL_IND, gsm48_mm_uplink_rel_ind}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_VGCS) | + SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), + GSM48_RR_REL_IND, gsm48_mm_group_rel_ind}, + /* other (also wait for network command) */ {ALL_STATES, GSM48_RR_REL_IND, gsm48_mm_rel_other}, @@ -4065,6 +4626,7 @@ uint8_t pdisc = gh->proto_discr & 0x0f; uint8_t msg_type = gh->msg_type & 0xbf; uint8_t transaction_id; + uint32_t callref; struct gsm48_mm_conn *conn; struct gsm48_mmxx_hdr *mmh; int msg_supported = 0; /* determine, if message is supported at all */ @@ -4087,7 +4649,7 @@ /* pull the RR header */ msgb_pull(msg, sizeof(struct gsm48_rr_hdr)); - /* create transaction (if not exists) and push MM header to message */ + /* create transaction (if not exists) and push message */ switch (pdisc) { case GSM48_PDISC_CC: rr_prim = GSM48_MMCC_DATA_IND; @@ -4113,34 +4675,39 @@ transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; /* flip */ - /* find transaction, if any */ - conn = mm_conn_by_id(mm, pdisc, transaction_id); + if (mm->vgcs.enabled) { + /* Ongoing group call. */ + callref = mm->vgcs.callref; + } else { + /* find transaction, if any */ + conn = mm_conn_by_id(mm, pdisc, transaction_id); - /* create MM connection instance */ - if (!conn) { - /* if MT calls are not supported with protocol */ - if (rr_est == -1) { - LOGP(DMM, LOGL_ERROR, "No MO connection for pdisc=%d, " - "transaction_id=%d\n", pdisc, transaction_id); - msgb_free(msg); - return -EINVAL; + /* create MM connection instance */ + if (!conn) { + /* if MT calls are not supported with protocol */ + if (rr_est == -1) { + LOGP(DMM, LOGL_ERROR, "No MO connection for pdisc=%d, transaction_id=%d\n", + pdisc, transaction_id); + msgb_free(msg); + return -EINVAL; + } + conn = mm_conn_new(mm, pdisc, transaction_id, sapi, mm_conn_new_ref++); + rr_prim = rr_est; } - - conn = mm_conn_new(mm, pdisc, transaction_id, sapi, mm_conn_new_ref++); if (!conn) { msgb_free(msg); return -ENOMEM; } - rr_prim = rr_est; + callref = conn->ref; } /* push new header */ msgb_push(msg, sizeof(struct gsm48_mmxx_hdr)); mmh = (struct gsm48_mmxx_hdr *)msg->data; mmh->msg_type = rr_prim; - mmh->ref = conn->ref; - mmh->transaction_id = conn->transaction_id; - mmh->sapi = conn->sapi; + mmh->ref = callref; + mmh->transaction_id = transaction_id; + mmh->sapi = sapi; /* go MM CONN ACTIVE state */ if (mm->state == GSM48_MM_ST_WAIT_NETWORK_CMD || @@ -4272,6 +4839,10 @@ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_start}, + /* 4.2.2.7 Receiving Group Call (Normal service) */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL), + GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_vgcs}, + /* 4.2.2.2 Attempt to update / Loc. upd. needed */ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) | SBIT(GSM48_MM_SST_LOC_UPD_NEEDED), @@ -4307,6 +4878,10 @@ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_delay_per}, /* 4.4.2 */ + /* 4.2.2.8 Receiving Group Call (Limited service) */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_LIMITED), + GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_vgcs}, + /* 4.2.2.4 No IMSI */ /* 4.2.2.5 PLMN search, normal service */ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), @@ -4342,12 +4917,15 @@ {SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES, /* silently detach */ GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_end}, + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS) | + SBIT(GSM48_MM_ST_WAIT_RR_CONN_VGCS), ALL_STATES, /* uplink access */ + GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_vgcs}, + {SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN) | SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) | SBIT(GSM48_MM_ST_PROCESS_CM_SERV_P) | SBIT(GSM48_MM_ST_WAIT_REEST) | SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON) | - SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS) | SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES, /* we can release */ GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_release}, @@ -4402,6 +4980,17 @@ {ALL_STATES, ALL_STATES, GSM48_MM_EVENT_CLASSMARK_CHG, gsm48_mm_classm_chg}, #endif + + /* Group call notification event */ + {ALL_STATES, ALL_STATES, + GSM48_MM_EVENT_NOTIFICATION, gsm48_mm_notification}, + + /* Uplink free/busy while in group receive mode */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL) | SBIT(GSM48_MM_SST_RX_VGCS_LIMITED), + GSM48_MM_EVENT_UPLINK_BUSY, gsm48_mm_uplink_free}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL) | SBIT(GSM48_MM_SST_RX_VGCS_LIMITED), + GSM48_MM_EVENT_UPLINK_FREE, gsm48_mm_uplink_free}, }; #define EVENTSLLEN \ -- To view, visit https://gerrit.osmocom.org/c/osmocom-bb/+/34492?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmocom-bb Gerrit-Branch: master Gerrit-Change-Id: I05957182a57423ad947ab200b52f65fde859e110 Gerrit-Change-Number: 34492 Gerrit-PatchSet: 15 Gerrit-Owner: jolly <andreas(a)eversberg.eu> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-MessageType: merged
1 0
0 0
[L] Change in osmocom-bb[master]: ASCI: Add group transmit mode support to RR layer
by laforge 03 Oct '23

03 Oct '23
laforge has submitted this change. ( https://gerrit.osmocom.org/c/osmocom-bb/+/34545?usp=email ) Change subject: ASCI: Add group transmit mode support to RR layer ...................................................................... ASCI: Add group transmit mode support to RR layer This allows the upper layer to estabish and release connection on the uplink of a voice group call. Related: OS#5364 Change-Id: I9b62eef5d877e5d9dcf349717efd2cce28862c58 --- M src/host/layer23/include/osmocom/bb/common/settings.h M src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h M src/host/layer23/src/mobile/gsm48_rr.c 3 files changed, 743 insertions(+), 65 deletions(-) Approvals: laforge: Looks good to me, approved pespin: Looks good to me, but someone else must approve Jenkins Builder: Verified diff --git a/src/host/layer23/include/osmocom/bb/common/settings.h b/src/host/layer23/include/osmocom/bb/common/settings.h index c5469c1..884578b 100644 --- a/src/host/layer23/include/osmocom/bb/common/settings.h +++ b/src/host/layer23/include/osmocom/bb/common/settings.h @@ -188,6 +188,9 @@ /* Timeout for GSM 03.22 C7 state */ uint8_t any_timeout; + + /* ASCI settings */ + bool uplink_release_local; }; struct gsm_settings_abbrev { 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 1429fd3..16f7fb3 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h +++ b/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h @@ -10,6 +10,9 @@ #define T200_DCCH_SHARED 2 /* SDCCH shares SAPI 0 and 3 */ #define T200_ACCH 2 /* SACCH SAPI 3 */ +/* GSM 04.08 RR timers */ +#define GSM_T3128_MS 1, 0 /* Uplink investigation timer. */ +#define GSM_T3130_MS 5, 0 /* Uplink access timeout. */ /* GSM 04.07 9.1.2 */ @@ -221,6 +224,14 @@ 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 */ + bool uplink_free; /* Is set, if uplink is currently free. */ + uint8_t uic; /* UIC to use for access burst (-1 for BSIC) */ + bool uplink_access; /* The network wants us to send listener access bursts. */ + struct osmo_timer_list t_ul_free; /* Uplink free timer. (480ms timer) */ + struct osmo_timer_list t3128; /* Uplink investigation timer. */ + struct osmo_timer_list t3130; /* Uplink access timer. */ + uint8_t uplink_tries; /* Counts number of tries to access the uplink. */ + uint8_t uplink_counter; /* Counts number of access bursts per 'try'. */ } vgcs; }; diff --git a/src/host/layer23/src/mobile/gsm48_rr.c b/src/host/layer23/src/mobile/gsm48_rr.c index c78993b..a515338 100644 --- a/src/host/layer23/src/mobile/gsm48_rr.c +++ b/src/host/layer23/src/mobile/gsm48_rr.c @@ -50,6 +50,22 @@ * 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: * @@ -132,6 +148,13 @@ 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 @@ -871,6 +894,36 @@ 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; @@ -923,6 +976,33 @@ 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)) { @@ -979,6 +1059,30 @@ } } +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 */ @@ -1612,6 +1716,401 @@ } /* + * 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 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 (Use TMSI if available.) */ + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_TMSI, false); + + /* start establishmnet */ + return gsm48_send_rsl(ms, RSL_MT_EST_REQ, nmsg, 0); +} + +/* * random access */ @@ -1636,7 +2135,7 @@ && (!cs->selected || (cs->state != GSM322_C3_CAMPED_NORMALLY && cs->state != GSM322_C7_CAMPED_ANY_CELL))) { LOGP(DRR, LOGL_INFO, "Paging, but not camping, ignore.\n"); - return -EINVAL; + return -EINVAL; } /* ignore channel request while not camping on a cell */ @@ -3357,7 +3856,7 @@ gsm48_rr_tx_meas_rep(ms); /* establish */ - LOGP(DRR, LOGL_INFO, "establishing channel in dedicated mode\n"); + 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, @@ -3701,6 +4200,12 @@ 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: @@ -3770,6 +4275,9 @@ 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); @@ -4831,6 +5339,16 @@ 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) { @@ -4853,17 +5371,27 @@ return 0; } -/* channel is resumed in dedicated mode */ +/* Channel is resumed in dedicated mode or uplink is estabished. */ static int gsm48_rr_estab_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; - LOGP(DRR, LOGL_INFO, "data link is resumed\n"); + if (rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) { + LOGP(DRR, LOGL_INFO, "data link established on uplink\n"); - /* transmit queued frames during ho / ass transition */ - gsm48_rr_dequeue_down(ms); + /* Confirm uplink access. */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_UPLINK_CNF); + if (nmsg) + gsm48_rr_upmsg(ms, nmsg); + } else { + LOGP(DRR, LOGL_INFO, "data link is resumed\n"); - rr->modify_state = GSM48_RR_MOD_NONE; + /* transmit queued frames during ho / ass transition */ + gsm48_rr_dequeue_down(ms); + + rr->modify_state = GSM48_RR_MOD_NONE; + } return 0; } @@ -5159,6 +5687,9 @@ goto reject; } + /* Initial uplink state. */ + rr->vgcs.uplink_free = false; + /* Set group state to receive mode. */ rr->vgcs.group_state = GSM48_RR_GST_RECEIVE; @@ -5191,6 +5722,38 @@ 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; @@ -5214,6 +5777,21 @@ 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) { @@ -5226,6 +5804,89 @@ 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) { @@ -5311,6 +5972,9 @@ 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; @@ -5427,6 +6091,8 @@ 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: @@ -5462,6 +6128,10 @@ } 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: @@ -5619,6 +6289,12 @@ 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: @@ -5717,6 +6393,12 @@ 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) @@ -5979,11 +6661,6 @@ {SBIT(GSM48_RR_ST_DEDICATED), RSL_MT_SUSP_CONF, gsm48_rr_susp_cnf_dedicated}, -#if 0 - {SBIT(GSM48_RR_ST_DEDICATED), - RSL_MT_CHAN_CNF, gsm48_rr_rand_acc_cnf_dedicated}, -#endif - {SBIT(GSM48_RR_ST_DEDICATED), RSL_MT_ERROR_IND, gsm48_rr_mdl_error_ind}, }; @@ -6080,11 +6757,24 @@ "%s\n", ms->name, rsl_msg_name(msg_type), gsm48_rr_state_names[rr->state]); - if (rr->state == GSM48_RR_ST_CONN_PEND - && msg_type == RSL_MT_CHAN_CONF) { - rc = gsm48_rr_tx_rand_acc(ms, msg); - msgb_free(msg); - return rc; + if (msg_type == RSL_MT_CHAN_CONF) { + /* Recevie confirm to get the FN when the access burst was transmitted on VGCS channel. */ + if (rr->state == GSM48_RR_ST_CONN_PEND && rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) { + rc = gsm48_rr_uplink_access(ms, msg); + msgb_free(msg); + return rc; + } + /* Ignore subsequent access bursts in dedicated group transmit mode. */ + if (rr->state == GSM48_RR_ST_DEDICATED && rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) { + msgb_free(msg); + return 0; + } + /* Recevie confirm to get the FN when the access burst was transmitted on CCCH. */ + if (rr->state == GSM48_RR_ST_CONN_PEND) { + rc = gsm48_rr_tx_rand_acc(ms, msg); + msgb_free(msg); + return rc; + } } LOGP(DRSL, LOGL_NOTICE, "RSLms message unhandled\n"); @@ -6144,6 +6834,12 @@ {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 \ @@ -6285,6 +6981,9 @@ 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); @@ -6331,54 +7030,6 @@ osmo_timer_schedule(&rr->t3124, sec, micro); } -/* send HANDOVER ACCESS burst (9.1.14) */ -static int gsm48_rr_tx_hando_access(struct osmocom_ms *ms) -{ - nmsg = msgb_alloc_headroom(20, 16, "HAND_ACCESS"); - if (!nmsg) - return -ENOMEM; - *msgb_put(nmsg, 1) = rr->hando_ref; - todo burst - return gsm48_send_rsl(ms, RSL_MT_RAND_ACC_REQ, nmsg, 0); -} - -/* send next channel request in dedicated state */ -static int gsm48_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg) -{ - struct gsm48_rrlayer *rr = &ms->rrlayer; - struct msgb *nmsg; - int s; - - if (rr->modify_state != GSM48_RR_MOD_HANDO) { - LOGP(DRR, LOGL_NOTICE, "Random access confirm, but not in handover state.\n"); - return 0; - } - - /* send up to four handover access bursts */ - if (rr->hando_acc_left) { - rr->hando_acc_left--; - gsm48_rr_tx_hando_access(ms); - return; - } - - /* start timer for sending next HANDOVER ACCESS bursts afterwards */ - if (!osmo_timer_pending(&rr->t3124)) { - if (allocated channel is SDCCH) - start_rr_t3124(rr, GSM_T3124_675); - else - start_rr_t3124(rr, GSM_T3124_320); - } - if (!rr->n_chan_req) { - start_rr_t3126(rr, 5, 0); /* TODO improve! */ - return 0; - } - rr->n_chan_req--; - - /* wait for PHYSICAL INFORMATION message or T3124 timeout */ - return 0; - -} - #endif int gsm48_rr_tx_voice(struct osmocom_ms *ms, struct msgb *msg) -- To view, visit https://gerrit.osmocom.org/c/osmocom-bb/+/34545?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmocom-bb Gerrit-Branch: master Gerrit-Change-Id: I9b62eef5d877e5d9dcf349717efd2cce28862c58 Gerrit-Change-Number: 34545 Gerrit-PatchSet: 6 Gerrit-Owner: jolly <andreas(a)eversberg.eu> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-MessageType: merged
1 0
0 0
[S] Change in osmocom-bb[master]: ASCI: Add protocol type to trans_find_by_callref() function
by laforge 03 Oct '23

03 Oct '23
laforge has submitted this change. ( https://gerrit.osmocom.org/c/osmocom-bb/+/34493?usp=email ) ( 5 is the latest approved patch-set. No files were changed between the latest approved patch-set and the submitted one. )Change subject: ASCI: Add protocol type to trans_find_by_callref() function ...................................................................... ASCI: Add protocol type to trans_find_by_callref() function This is required, because different protocols may share the same callref, but use different protocols. E.g. a voice group call can share the same callref with a voice broadcast call, but these calls are different transactions. Related: OS#5364 Change-Id: Ifea3a81aae3b4ae897851f867b13fa987c8cbe11 --- M src/host/layer23/include/osmocom/bb/mobile/transaction.h M src/host/layer23/src/mobile/gsm411_sms.c M src/host/layer23/src/mobile/gsm480_ss.c M src/host/layer23/src/mobile/gsm48_cc.c M src/host/layer23/src/mobile/transaction.c 5 files changed, 22 insertions(+), 7 deletions(-) Approvals: fixeria: Looks good to me, approved Jenkins Builder: Verified pespin: Looks good to me, but someone else must approve diff --git a/src/host/layer23/include/osmocom/bb/mobile/transaction.h b/src/host/layer23/include/osmocom/bb/mobile/transaction.h index 8c06d5d..1c998cd 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/transaction.h +++ b/src/host/layer23/include/osmocom/bb/mobile/transaction.h @@ -62,7 +62,7 @@ struct gsm_trans *trans_find_by_id(struct osmocom_ms *ms, uint8_t proto, uint8_t trans_id); -struct gsm_trans *trans_find_by_callref(struct osmocom_ms *ms, +struct gsm_trans *trans_find_by_callref(struct osmocom_ms *ms, uint8_t protocol, uint32_t callref); struct gsm_trans *trans_alloc(struct osmocom_ms *ms, diff --git a/src/host/layer23/src/mobile/gsm411_sms.c b/src/host/layer23/src/mobile/gsm411_sms.c index 64d098c..a5454b7 100644 --- a/src/host/layer23/src/mobile/gsm411_sms.c +++ b/src/host/layer23/src/mobile/gsm411_sms.c @@ -914,7 +914,7 @@ struct gsm_trans *trans; int rc = 0; - trans = trans_find_by_callref(ms, mmh->ref); + trans = trans_find_by_callref(ms, GSM48_PDISC_SMS, mmh->ref); if (!trans) { LOGP(DLSMS, LOGL_INFO, " -> (new transaction sapi=%d)\n", sapi); trans = trans_alloc(ms, GSM48_PDISC_SMS, mmh->transaction_id, diff --git a/src/host/layer23/src/mobile/gsm480_ss.c b/src/host/layer23/src/mobile/gsm480_ss.c index 202e1e7..bf99667 100644 --- a/src/host/layer23/src/mobile/gsm480_ss.c +++ b/src/host/layer23/src/mobile/gsm480_ss.c @@ -1240,7 +1240,7 @@ struct gsm_trans *trans; int rc = 0; - trans = trans_find_by_callref(ms, mmh->ref); + trans = trans_find_by_callref(ms, GSM48_PDISC_NC_SS, mmh->ref); if (!trans) { LOGP(DSS, LOGL_INFO, " -> (new transaction)\n"); trans = trans_alloc(ms, GSM48_PDISC_NC_SS, mmh->transaction_id, diff --git a/src/host/layer23/src/mobile/gsm48_cc.c b/src/host/layer23/src/mobile/gsm48_cc.c index 43c93fc..190eb91 100644 --- a/src/host/layer23/src/mobile/gsm48_cc.c +++ b/src/host/layer23/src/mobile/gsm48_cc.c @@ -1937,7 +1937,7 @@ data->msg_type = msg_type; /* Find callref */ - trans = trans_find_by_callref(ms, data->callref); + trans = trans_find_by_callref(ms, GSM48_PDISC_CC, data->callref); if (!trans) { /* check for SETUP message */ @@ -2147,7 +2147,7 @@ struct gsm_trans *trans; int rc = 0; - trans = trans_find_by_callref(ms, mmh->ref); + trans = trans_find_by_callref(ms, GSM48_PDISC_CC, mmh->ref); if (!trans) { trans = trans_alloc(ms, GSM48_PDISC_CC, mmh->transaction_id, mmh->ref); diff --git a/src/host/layer23/src/mobile/transaction.c b/src/host/layer23/src/mobile/transaction.c index 2fb910a..712a8ee 100644 --- a/src/host/layer23/src/mobile/transaction.c +++ b/src/host/layer23/src/mobile/transaction.c @@ -44,13 +44,13 @@ return NULL; } -struct gsm_trans *trans_find_by_callref(struct osmocom_ms *ms, +struct gsm_trans *trans_find_by_callref(struct osmocom_ms *ms, uint8_t protocol, uint32_t callref) { struct gsm_trans *trans; llist_for_each_entry(trans, &ms->trans_list, entry) { - if (trans->callref == callref) + if (trans->protocol == protocol && trans->callref == callref) return trans; } return NULL; -- To view, visit https://gerrit.osmocom.org/c/osmocom-bb/+/34493?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmocom-bb Gerrit-Branch: master Gerrit-Change-Id: Ifea3a81aae3b4ae897851f867b13fa987c8cbe11 Gerrit-Change-Number: 34493 Gerrit-PatchSet: 15 Gerrit-Owner: jolly <andreas(a)eversberg.eu> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-MessageType: merged
1 0
0 0
[XL] Change in osmocom-bb[master]: ASCI: Add GCC/BCC layer to support voice group/broadcast calls
by laforge 03 Oct '23

03 Oct '23
laforge has submitted this change. ( https://gerrit.osmocom.org/c/osmocom-bb/+/34494?usp=email ) ( 14 is the latest approved patch-set. No files were changed between the latest approved patch-set and the submitted one. )Change subject: ASCI: Add GCC/BCC layer to support voice group/broadcast calls ...................................................................... ASCI: Add GCC/BCC layer to support voice group/broadcast calls Related: OS#5364 Change-Id: I22eacd018f18c18ba83dbd3dd874cf029344674d --- M src/host/layer23/include/osmocom/bb/mobile/Makefile.am A src/host/layer23/include/osmocom/bb/mobile/gsm44068_gcc_bcc.h M src/host/layer23/include/osmocom/bb/mobile/transaction.h M src/host/layer23/src/mobile/Makefile.am M src/host/layer23/src/mobile/app_mobile.c M src/host/layer23/src/mobile/gsm411_sms.c A src/host/layer23/src/mobile/gsm44068_gcc_bcc.c M src/host/layer23/src/mobile/gsm480_ss.c M src/host/layer23/src/mobile/gsm48_cc.c M src/host/layer23/src/mobile/gsm48_mm.c M src/host/layer23/src/mobile/transaction.c 11 files changed, 2,069 insertions(+), 3 deletions(-) Approvals: Jenkins Builder: Verified fixeria: Looks good to me, approved pespin: Looks good to me, but someone else must approve diff --git a/src/host/layer23/include/osmocom/bb/mobile/Makefile.am b/src/host/layer23/include/osmocom/bb/mobile/Makefile.am index 14694bb..534b865 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/Makefile.am +++ b/src/host/layer23/include/osmocom/bb/mobile/Makefile.am @@ -1,4 +1,4 @@ noinst_HEADERS = gsm322.h gsm480_ss.h gsm411_sms.h gsm48_cc.h gsm48_mm.h \ - gsm48_rr.h mncc.h \ + gsm48_rr.h mncc.h gsm44068_gcc_bcc.h \ transaction.h vty.h mncc_sock.h mncc_ms.h primitives.h \ app_mobile.h voice.h gapk_io.h diff --git a/src/host/layer23/include/osmocom/bb/mobile/gsm44068_gcc_bcc.h b/src/host/layer23/include/osmocom/bb/mobile/gsm44068_gcc_bcc.h new file mode 100644 index 0000000..e9889b3 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/mobile/gsm44068_gcc_bcc.h @@ -0,0 +1,43 @@ +/* VGCS/VBS call control */ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info(a)sysmocom.de> + * All Rights Reserved + * + * SPDX-License-Identifier: AGPL-3.0+ + * + * Author: Andreas Eversberg + * + * 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/licenses/>. + */ +#pragma once + +#define GSM44068_ALLOC_SIZE 2048 +#define GSM44068_ALLOC_HEADROOM 256 + +static inline struct msgb *gsm44068_msgb_alloc_name(const char *name) +{ + return msgb_alloc_headroom(GSM44068_ALLOC_SIZE, GSM44068_ALLOC_HEADROOM, name); +} + +int gsm44068_gcc_init(struct osmocom_ms *ms); +int gsm44068_gcc_exit(struct osmocom_ms *ms); +int gsm44068_rcv_gcc_bcc(struct osmocom_ms *ms, struct msgb *msg); +int gsm44068_rcv_mm_idle(struct osmocom_ms *ms); +struct gsm_trans *trans_find_ongoing_gcc_bcc(struct osmocom_ms *ms); +int gcc_bcc_call(struct osmocom_ms *ms, uint8_t protocol, const char *number); +int gcc_leave(struct osmocom_ms *ms); +int gcc_bcc_hangup(struct osmocom_ms *ms); +int gcc_talk(struct osmocom_ms *ms); +int gcc_listen(struct osmocom_ms *ms); +int gsm44068_dump_calls(struct osmocom_ms *ms, void (*print)(void *, const char *, ...), void *priv); diff --git a/src/host/layer23/include/osmocom/bb/mobile/transaction.h b/src/host/layer23/include/osmocom/bb/mobile/transaction.h index 1c998cd..103ae4e 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/transaction.h +++ b/src/host/layer23/include/osmocom/bb/mobile/transaction.h @@ -27,7 +27,6 @@ union { struct { - /* current call state */ int state; @@ -55,6 +54,26 @@ struct gsm_sms *sms; } sms; + struct { + /* VGCS/VBS state machine */ + struct osmo_fsm_inst *fi; + + /* Call State (See Table 9.3 of TS 144.068) */ + uint8_t call_state; + + /* State attributes (See Table 9.7 of TS 144.068) */ + uint8_t d_att, u_att, comm, orig; + + /* Channel description last received via notification */ + bool ch_desc_present; + struct gsm48_chan_desc ch_desc; + + /* Flag to store termination request from upper layer. */ + bool termination; + + /* Flag to tell the state machine that call changes from separate link to group receive mode. */ + bool receive_after_sl; + } gcc; }; }; diff --git a/src/host/layer23/src/mobile/Makefile.am b/src/host/layer23/src/mobile/Makefile.am index 55df667..7b05f65 100644 --- a/src/host/layer23/src/mobile/Makefile.am +++ b/src/host/layer23/src/mobile/Makefile.am @@ -23,6 +23,7 @@ gsm480_ss.c \ gsm411_sms.c \ gsm48_cc.c \ + gsm44068_gcc_bcc.c \ gsm48_mm.c \ gsm48_rr.c \ gsm414.c \ diff --git a/src/host/layer23/src/mobile/app_mobile.c b/src/host/layer23/src/mobile/app_mobile.c index 9ce1ad2..b08976c 100644 --- a/src/host/layer23/src/mobile/app_mobile.c +++ b/src/host/layer23/src/mobile/app_mobile.c @@ -35,6 +35,7 @@ #include <osmocom/bb/mobile/gsm480_ss.h> #include <osmocom/bb/mobile/gsm48_mm.h> #include <osmocom/bb/mobile/gsm48_cc.h> +#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h> #include <osmocom/bb/mobile/gsm411_sms.h> #include <osmocom/bb/mobile/gsm322.h> #include <osmocom/bb/mobile/vty.h> @@ -212,6 +213,7 @@ gsm48_cc_exit(ms); gsm480_ss_exit(ms); gsm411_sms_exit(ms); + gsm44068_gcc_exit(ms); gsm_sim_exit(ms); lapdm_channel_exit(&ms->lapdm_channel); @@ -253,6 +255,7 @@ gsm48_cc_init(ms); gsm480_ss_init(ms); gsm411_sms_init(ms); + gsm44068_gcc_init(ms); gsm_voice_init(ms); gsm_subscr_init(ms); gsm48_rr_init(ms); diff --git a/src/host/layer23/src/mobile/gsm411_sms.c b/src/host/layer23/src/mobile/gsm411_sms.c index a5454b7..a21133b 100644 --- a/src/host/layer23/src/mobile/gsm411_sms.c +++ b/src/host/layer23/src/mobile/gsm411_sms.c @@ -34,6 +34,7 @@ #include <osmocom/bb/mobile/mncc.h> #include <osmocom/bb/mobile/transaction.h> #include <osmocom/bb/mobile/gsm411_sms.h> +#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h> #include <osmocom/gsm/gsm0411_utils.h> #include <osmocom/core/talloc.h> #include <osmocom/bb/mobile/vty.h> @@ -651,6 +652,14 @@ return -EIO; } + /* ASCI call does not allow other transactions */ + if (trans_find_ongoing_gcc_bcc(ms)) { + LOGP(DLSMS, LOGL_ERROR, "Phone is busy doing ASCI call\n"); + gsm411_sms_report(ms, sms, GSM411_RP_CAUSE_MO_TEMP_FAIL); + sms_free(sms); + return -EIO; + } + /* allocate transaction with dummy reference */ transaction_id = trans_assign_trans_id(ms, GSM48_PDISC_SMS, 0); if (transaction_id < 0) { diff --git a/src/host/layer23/src/mobile/gsm44068_gcc_bcc.c b/src/host/layer23/src/mobile/gsm44068_gcc_bcc.c new file mode 100644 index 0000000..fa0e7bd --- /dev/null +++ b/src/host/layer23/src/mobile/gsm44068_gcc_bcc.c @@ -0,0 +1,1951 @@ +/* Handle VGCS/VBCS calls. (Voice Group/Broadcast Call Service). */ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info(a)sysmocom.de> + * All Rights Reserved + * + * SPDX-License-Identifier: AGPL-3.0+ + * + * Author: Andreas Eversberg + * + * 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/licenses/>. + */ + +/* Notes on the state machine: + * + * The state machine is different from the diagram depicted in the specs. + * This is because there are some messages missing and some state transitions + * are different or not shown. + * + * A call that has no channel is answered without joining the group channel. + * If it comes available, the establishment is performed and the U4 is entered. + * + * Uplink control is not described in the diagram. Talking/listening is + * requested by user and can be rejected by MM layer, if talking is not + * allowed. + * + * We can be sure that there is no other MM connection while doing VGCS call + * establishment: MMxx-EST-REQ is only accpepted, if there is no MM connection. + * We block calls from user, if there is some other transaction, which is not + * in state U3. Also we accept incoming indications any time and create + * transactions that go to state U3. + * + * If the upper layer or lower layer requests another call/SMS/SS while VGCS + * call is ongoing, this may cause undefined behaviour. + * + */ + +#include <stdint.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/utils.h> +#include <osmocom/gsm/gsm48.h> +#include <osmocom/gsm/protocol/gsm_44_068.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/fsm.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/mobile/mncc.h> +#include <osmocom/bb/mobile/transaction.h> +#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h> +#include <osmocom/bb/mobile/voice.h> +#include <osmocom/bb/mobile/vty.h> +#include <l1ctl_proto.h> + +#define S(x) (1 << (x)) + +#define LOG_GCC(trans, level, fmt, args...) \ + LOGP(((trans)->protocol == GSM48_PDISC_GROUP_CC) ? DGCC : DBCC, level, \ + ((trans)->protocol == GSM48_PDISC_GROUP_CC) ? ("VGCS callref %u: " fmt) : ("VBS callref %u: " fmt), \ + (trans)->callref, ##args) +#define LOG_GCC_PR(protocol, ref, level, fmt, args...) \ + LOGP((protocol == GSM48_PDISC_GROUP_CC) ? DGCC : DBCC, level, \ + (protocol == GSM48_PDISC_GROUP_CC) ? ("VGCS callref %u: " fmt) : ("VBS callref %u: " fmt), \ + ref, ##args) + +/* + * init + */ + +static struct osmo_fsm vgcs_gcc_fsm; +static struct osmo_fsm vgcs_bcc_fsm; + +int gsm44068_gcc_init(struct osmocom_ms *ms) +{ + OSMO_ASSERT(osmo_fsm_register(&vgcs_gcc_fsm) == 0); + OSMO_ASSERT(osmo_fsm_register(&vgcs_bcc_fsm) == 0); + + LOGP(DGCC, LOGL_INFO, "init GCC/BCC\n"); + + return 0; +} + +int gsm44068_gcc_exit(struct osmocom_ms *ms) +{ + struct gsm_trans *trans, *trans2; + + LOGP(DGCC, LOGL_INFO, "exit GCC/BCC processes for %s\n", ms->name); + + llist_for_each_entry_safe(trans, trans2, &ms->trans_list, entry) { + if (trans->protocol == GSM48_PDISC_GROUP_CC || trans->protocol == GSM48_PDISC_BCAST_CC) { + LOG_GCC(trans, LOGL_NOTICE, "Free pendig CC-transaction.\n"); + trans_free(trans); + } + } + + return 0; +} + + +/* + * messages + */ + +/* TS 44.068 Chapter 6.1.2.1 */ +enum vgcs_gcc_fsm_states { + VGCS_GCC_ST_U0_NULL = 0, + VGCS_GCC_ST_U0p_MM_CONNECTION_PENDING, + VGCS_GCC_ST_U1_GROUP_CALL_INITIATED, + VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE, /* sepeate link */ + VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE, /* wait for receive mode */ + VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE, /* receive mode / U6 @ BCC */ + VGCS_GCC_ST_U2ws_GROUP_CALL_ACTIVE, /* wait for send and receive mode */ + VGCS_GCC_ST_U2sr_GROUP_CALL_ACTIVE, /* send and receive mode */ + VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE, /* no channel */ + VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, + VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST, + VGCS_GCC_ST_U5_TERMINATION_REQUESTED, +}; + +/* TS 44.068 Figure 6.1 (additional events added) */ +enum vgcs_gcc_fsm_event { + VGCS_GCC_EV_SETUP_REQ, /* calling user initiates call */ + VGCS_GCC_EV_TERM_REQ, /* calling user requests termination */ + VGCS_GCC_EV_MM_EST_CNF, /* MM connection established */ + VGCS_GCC_EV_MM_EST_REJ, /* MM connection failed */ + VGCS_GCC_EV_DI_TERMINATION, /* network acknowledges termination */ + VGCS_GCC_EV_DI_TERM_REJECT, /* network rejects termination */ + VGCS_GCC_EV_DI_CONNECT, /* network indicates connect */ + VGCS_GCC_EV_TIMEOUT, /* several timeout events */ + VGCS_GCC_EV_SETUP_IND, /* notification of ongoing call received */ + VGCS_GCC_EV_JOIN_GC_REQ, /* user wants to join ongoing call */ + VGCS_GCC_EV_JOIN_GC_CNF, /* MM confirms joining ongoing call */ + VGCS_GCC_EV_ABORT_REQ, /* user rejects or leaves call */ + VGCS_GCC_EV_ABORT_IND, /* MM indicates call gone / channel released or failed */ + VGCS_GCC_EV_TALK_REQ, /* user wants to talk */ + VGCS_GCC_EV_TALK_CNF, /* MM confirms talk */ + VGCS_GCC_EV_TALK_REJ, /* MM rejects talk */ + VGCS_GCC_EV_LISTEN_REQ, /* user wants to listen */ + VGCS_GCC_EV_LISTEN_CNF, /* MM confirms listen */ + VGCS_GCC_EV_MM_IDLE, /* MM layer becomes ready for new channel */ + VGCS_GCC_EV_UPLINK_FREE, /* MM layer indicates free uplink in group receive mode */ + VGCS_GCC_EV_UPLINK_BUSY, /* MM layer indicates busy uplink in group receive mode */ +}; + +static const struct value_string vgcs_gcc_fsm_event_names[] = { + OSMO_VALUE_STRING(VGCS_GCC_EV_SETUP_REQ), + OSMO_VALUE_STRING(VGCS_GCC_EV_TERM_REQ), + OSMO_VALUE_STRING(VGCS_GCC_EV_MM_EST_CNF), + OSMO_VALUE_STRING(VGCS_GCC_EV_MM_EST_REJ), + OSMO_VALUE_STRING(VGCS_GCC_EV_DI_TERMINATION), + OSMO_VALUE_STRING(VGCS_GCC_EV_DI_TERM_REJECT), + OSMO_VALUE_STRING(VGCS_GCC_EV_DI_CONNECT), + OSMO_VALUE_STRING(VGCS_GCC_EV_TIMEOUT), + OSMO_VALUE_STRING(VGCS_GCC_EV_SETUP_IND), + OSMO_VALUE_STRING(VGCS_GCC_EV_JOIN_GC_REQ), + OSMO_VALUE_STRING(VGCS_GCC_EV_JOIN_GC_CNF), + OSMO_VALUE_STRING(VGCS_GCC_EV_ABORT_REQ), + OSMO_VALUE_STRING(VGCS_GCC_EV_ABORT_IND), + OSMO_VALUE_STRING(VGCS_GCC_EV_TALK_REQ), + OSMO_VALUE_STRING(VGCS_GCC_EV_TALK_CNF), + OSMO_VALUE_STRING(VGCS_GCC_EV_TALK_REJ), + OSMO_VALUE_STRING(VGCS_GCC_EV_LISTEN_REQ), + OSMO_VALUE_STRING(VGCS_GCC_EV_LISTEN_CNF), + OSMO_VALUE_STRING(VGCS_GCC_EV_MM_IDLE), + OSMO_VALUE_STRING(VGCS_GCC_EV_UPLINK_FREE), + OSMO_VALUE_STRING(VGCS_GCC_EV_UPLINK_BUSY), + { } +}; + +/*! return string representation of GCC/BCC Message Type */ +static const char *gsm44068_gcc_msg_name(uint8_t msg_type) +{ + return get_value_string(osmo_gsm44068_msg_type_names, msg_type); +} + +#define TFU(param) ((param < 0) ? "unchanged" : ((param) ? "T" : "F")) + +/* Set state attributes and check if they are consistent with the current state. */ +static int set_state_attributes(struct gsm_trans *trans, int d_att, int u_att, int comm, int orig, int call_state) +{ + bool orig_t = false, comm_t = false; + + LOG_GCC(trans, LOGL_DEBUG, "Setting state attributes: D-ATT = %s, U-ATT = %s, COMM = %s, ORIG = %s.\n", + TFU(d_att), TFU(u_att), TFU(comm), TFU(orig)); + + /* Control Speaker. */ + if (d_att >= 0 && trans->gcc.d_att != d_att) { + LOG_GCC(trans, LOGL_DEBUG, "Switching Speaker to %d\n", d_att); + gsm48_rr_audio_mode(trans->ms, AUDIO_TX_MICROPHONE | (d_att * AUDIO_RX_SPEAKER)); + } + + if (d_att >= 0) + trans->gcc.d_att = d_att; + if (u_att >= 0) + trans->gcc.u_att = u_att; + if (comm >= 0) + trans->gcc.comm = comm; + if (orig >= 0) + trans->gcc.orig = orig; + if (call_state >= 0) + trans->gcc.call_state = call_state; + + switch (trans->gcc.fi->state) { + case VGCS_GCC_ST_U3_GROUP_CALL_PRESENT: + case VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST: + orig_t = orig; + comm_t = comm; + break; + case VGCS_GCC_ST_U0_NULL: + case VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE: + case VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE: + comm_t = comm; + break; + } + + if (orig_t) + LOG_GCC(trans, LOGL_ERROR, "ORIG = T is inconsistent with states U3 and U4. Please fix!"); + + if (comm_t) + LOG_GCC(trans, LOGL_ERROR, + "COMM = T is inconsistent with states U0, U3, U4, U2nc and U2r. Please fix!"); + + return (orig_t || comm_t) ? -EINVAL : 0; +} + +static void vgcs_vty_notify(struct gsm_trans *trans, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + +static void vgcs_vty_notify(struct gsm_trans *trans, const char *fmt, ...) +{ + struct osmocom_ms *ms = trans->ms; + char buffer[1000]; + va_list args; + + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer) - 1, fmt, args); + buffer[sizeof(buffer) - 1] = '\0'; + va_end(args); + + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "%s call %d: %s", (trans->protocol == GSM48_PDISC_GROUP_CC) ? "Group" : "Broadcast", + trans->callref, buffer); +} + +/* + * messages + */ + +/* Send MMxx-GROUP-REQ to MM. */ +static int vgcs_group_req(struct gsm_trans *trans) +{ + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + uint16_t msg_type = (trans->protocol == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_GROUP_REQ : GSM48_MMBCC_GROUP_REQ; + + LOG_GCC(trans, LOGL_INFO, "Sending %s.\n", get_mmxx_name(msg_type)); + + OSMO_ASSERT(trans->gcc.ch_desc_present); + + nmsg = gsm48_mmxx_msgb_alloc(msg_type, trans->callref, trans->transaction_id, 0); + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *) nmsg->data; + nmmh->ch_desc_present = trans->gcc.ch_desc_present; + memcpy(&nmmh->ch_desc, &trans->gcc.ch_desc, sizeof(nmmh->ch_desc)); + + return gsm48_mmxx_downmsg(trans->ms, nmsg); +} + +/* Send MMxx-EST-REQ to MM. */ +static int vgcs_est_req(struct gsm_trans *trans) +{ + struct msgb *nmsg; + uint16_t msg_type = (trans->protocol == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_EST_REQ : GSM48_MMBCC_EST_REQ; + + LOG_GCC(trans, LOGL_INFO, "Sending %s.\n", get_mmxx_name(msg_type)); + + nmsg = gsm48_mmxx_msgb_alloc(msg_type, trans->callref, trans->transaction_id, 0); + if (!nmsg) + return -ENOMEM; + + return gsm48_mmxx_downmsg(trans->ms, nmsg); +} + +/* Push message and send MMxx-DATA-REQ to MM. */ +static int vgcs_data_req(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_mmxx_hdr *mmh; + uint16_t msg_type = (trans->protocol == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_DATA_REQ : GSM48_MMBCC_DATA_REQ; + + /* push RR header */ + msg->l3h = msg->data; + msgb_push(msg, sizeof(struct gsm48_mmxx_hdr)); + mmh = (struct gsm48_mmxx_hdr *)msg->data; + mmh->msg_type = msg_type; + mmh->ref = trans->callref; + mmh->transaction_id = trans->transaction_id; + mmh->sapi = 0; + + /* send message to MM */ + return gsm48_mmxx_downmsg(trans->ms, msg); +} + +/* Send MMxx-REL-REQ to MM. */ +static int vgcs_rel_req(struct gsm_trans *trans) +{ + struct msgb *nmsg; + uint16_t msg_type = (trans->protocol == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_REL_REQ : GSM48_MMBCC_REL_REQ; + + LOG_GCC(trans, LOGL_INFO, "Sending %s.\n", get_mmxx_name(msg_type)); + + nmsg = gsm48_mmxx_msgb_alloc(msg_type, trans->callref, trans->transaction_id, 0); + if (!nmsg) + return -ENOMEM; + + return gsm48_mmxx_downmsg(trans->ms, nmsg); +} + +/* Send MMxx-UPLINK-REQ to MM. */ +static int vgcs_uplink_req(struct gsm_trans *trans) +{ + struct msgb *nmsg; + uint16_t msg_type = (trans->protocol == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_UPLINK_REQ : GSM48_MMBCC_UPLINK_REQ; + + LOG_GCC(trans, LOGL_INFO, "Sending %s.\n", get_mmxx_name(msg_type)); + + nmsg = gsm48_mmxx_msgb_alloc(msg_type, trans->callref, trans->transaction_id, 0); + if (!nmsg) + return -ENOMEM; + + return gsm48_mmxx_downmsg(trans->ms, nmsg); +} + +/* Send MMxx-UPLINK-REL-REQ to MM. */ +static int vgcs_uplink_rel_req(struct gsm_trans *trans) +{ + struct msgb *nmsg; + uint16_t msg_type = (trans->protocol == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_UPLINK_REL_REQ + : GSM48_MMBCC_UPLINK_REL_REQ; + + LOG_GCC(trans, LOGL_INFO, "Sending %s.\n", get_mmxx_name(msg_type)); + + nmsg = gsm48_mmxx_msgb_alloc(msg_type, trans->callref, trans->transaction_id, 0); + if (!nmsg) + return -ENOMEM; + + return gsm48_mmxx_downmsg(trans->ms, nmsg); +} + +static void _add_callref_ie(struct msgb *msg, uint32_t callref, bool with_prio, uint8_t prio) +{ + uint32_t ie; + + ie = callref << 5; + if (with_prio) + ie |= 0x10 | (prio << 1); + msgb_put_u32(msg, ie); +} + +static void _add_user_user_ie(struct msgb *msg, uint8_t user_pdisc, uint8_t *user, uint8_t user_len) +{ + uint8_t *ie; + + ie = msgb_put(msg, user_len + 2); + *ie++ = GSM48_IE_USER_USER; + *ie++ = user_len; + memcpy(ie, user, user_len); +} + +#define GSM44068_IE_CALL_STATE 0xA0 +#define GSM44068_IE_STATE_ATTRS 0xB0 +#define GSM44068_IE_TALKER_PRIO 0xc0 + +static void _add_call_state_ie(struct msgb *msg, uint8_t call_state) +{ + msgb_put_u8(msg, GSM44068_IE_CALL_STATE | call_state); +} + +static void _add_state_attrs_ie(struct msgb *msg, uint8_t da, uint8_t ua, uint8_t comm, uint8_t oi) +{ + msgb_put_u8(msg, GSM44068_IE_STATE_ATTRS | (da << 3) | (ua << 2) | (comm << 1) | oi); +} + +static void _add_talker_prio_ie(struct msgb *msg, uint8_t talker_prio) +{ + msgb_put_u8(msg, GSM44068_IE_TALKER_PRIO | talker_prio); +} + +static void _add_cause_ie(struct msgb *msg, uint8_t cause, uint8_t *diag, uint8_t diag_len) +{ + uint8_t *ie; + + ie = msgb_put(msg, diag_len + 2); + *ie++ = diag_len + 1; + *ie++ = 0x80 | cause; + if (diag_len && diag) + memcpy(ie, diag, diag_len); +} + +static int _msg_too_short(struct gsm_trans *trans) +{ + LOG_GCC(trans, LOGL_ERROR, "MSG too short.\n"); + return -EINVAL; +} + +static int _ie_invalid(struct gsm_trans *trans) +{ + LOG_GCC(trans, LOGL_ERROR, "IE invalid.\n"); + return -EINVAL; +} + +/* 3GPP TS 44.068 Clause 8.4 */ +static int gsm44068_rx_set_parameter(struct gsm_trans *trans, struct msgb *msg, + uint8_t *da, uint8_t *ua, uint8_t *comm, uint8_t *oi) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int remaining_len = msgb_l3len(msg) - sizeof(*gh); + uint8_t *ie = gh->data; + + /* State attributes */ + if (remaining_len < 1) + return _msg_too_short(trans); + if (da) + *da = (ie[0] >> 3) & 0x1; + if (ua) + *ua = (ie[0] >> 2) & 0x1; + if (comm) + *comm = (ie[0] >> 1) & 0x1; + if (oi) + *oi = ie[0] & 0x1; + ie += 1; + + return 0; +} + +/* 3GPP TS 44.068 Clause 8.5 */ +static int gsm44068_tx_setup(struct gsm_trans *trans, uint32_t callref, bool with_prio, uint8_t prio, + uint8_t user_pdisc, uint8_t *user, uint8_t user_len, + bool with_talker_prio, uint8_t talker_prio) +{ + struct msgb *msg = gsm44068_msgb_alloc_name("GSM 44.068 TX SETUP"); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + LOG_GCC(trans, LOGL_INFO, "Sending SETUP.\n"); + + gh->proto_discr = trans->protocol | (trans->transaction_id << 4); + gh->msg_type = OSMO_GSM44068_MSGT_SETUP; + _add_callref_ie(msg, callref, with_prio, prio); + if (user_len && user) + _add_user_user_ie(msg, user_pdisc, user, user_len); + if (with_talker_prio) + _add_talker_prio_ie(msg, talker_prio); + + return vgcs_data_req(trans, msg); +} + +/* 3GPP TS 44.068 Clause 8.6 */ +static int gsm44068_tx_status(struct gsm_trans *trans, uint8_t cause, uint8_t *diag, uint8_t diag_len, + bool with_call_state, uint8_t call_state, bool with_state_attrs, + uint8_t da, uint8_t ua, uint8_t comm, uint8_t oi) +{ + struct msgb *msg = gsm44068_msgb_alloc_name("GSM 44.068 TX STATUS"); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + LOG_GCC(trans, LOGL_INFO, "Sending STATUS.\n"); + + gh->proto_discr = trans->protocol | (trans->transaction_id << 4); + gh->msg_type = OSMO_GSM44068_MSGT_STATUS; + _add_cause_ie(msg, cause, diag, diag_len); + if (with_call_state) + _add_call_state_ie(msg, call_state); + if (with_state_attrs) + _add_state_attrs_ie(msg, da, ua, comm, oi); + + return vgcs_data_req(trans, msg); +} + +/* 3GPP TS 44.068 Clause 8.7 and 8.8 */ +static int gsm44068_rx_termination(struct gsm_trans *trans, struct msgb *msg, uint8_t *cause, uint8_t *diag, uint8_t *diag_len) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int remaining_len = msgb_l3len(msg) - sizeof(*gh); + uint8_t *ie = gh->data; + uint8_t ie_len; + + /* Cause */ + if (remaining_len < 2 || ie[0] < remaining_len - 2) + return _msg_too_short(trans); + ie_len = ie[0]; + if (remaining_len < ie_len + 1) + return _msg_too_short(trans); + if (ie_len < 1) + return _ie_invalid(trans); + if (cause) + *cause = ie[1] & 0x7f; + if (diag && diag_len) { + *diag_len = ie_len - 1; + if (*diag_len) + memcpy(diag, ie + 2, ie_len - 1); + } + remaining_len -= ie_len + 1; + ie += ie_len + 1; + + return 0; +} + +/* 3GPP TS 44.068 Clause 8.9 */ +static int gsm44068_tx_termination_request(struct gsm_trans *trans, uint32_t callref, bool with_prio, uint8_t prio, + bool with_talker_prio, uint8_t talker_prio) +{ + struct msgb *msg = gsm44068_msgb_alloc_name("GSM 44.068 TX TERMINATION REQUEST"); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + LOG_GCC(trans, LOGL_INFO, "Sending TERMINATION REQUEST.\n"); + + gh->proto_discr = trans->protocol | (trans->transaction_id << 4); + gh->msg_type = OSMO_GSM44068_MSGT_TERMINATION_REQUEST; + _add_callref_ie(msg, callref, with_prio, prio); + if (with_talker_prio) + _add_talker_prio_ie(msg, talker_prio); + + return vgcs_data_req(trans, msg); +} + +/* 3GPP TS 44.018 Clause 9.1.48 */ +static int gsm44068_tx_uplink_release(struct gsm_trans *trans, uint8_t cause) +{ + struct msgb *msg = gsm44068_msgb_alloc_name("GSM 44.068 TX UPLINK RELEASE"); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + struct gsm48_uplink_release *ur = (struct gsm48_uplink_release *) msgb_put(msg, sizeof(*ur)); + + LOG_GCC(trans, LOGL_INFO, "UPLINK RELEASE (cause #%d)\n", cause); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_UPLINK_RELEASE; + ur->rr_cause = cause; + + return vgcs_data_req(trans, msg); +} + +/* + * GCC/BCC state machine + * + * For reference see Figure 6.1 of TS 44.068. + * + * Note: There are some events that are not depicted in the state diagram: + * + * "L: ABORT-IND" indicates closing/failing of radio channel. + * "H: TALK-REQ" request talking on the channel. + * "L: TALK-CNF" confirms talker. + * "L: TALK-REJ" rejects talker. + * "H: LISTEN-REQ" request listening. + * "L: LISTEN-CNF" confirms listening. + * + */ + +/* Table 6.1 of TS 44.068 */ +#define T_NO_CHANNEL 3 +#define T_MM_EST 7 +#define T_TERM 10 +#define T_CONN_REQ 10 + +static void vgcs_gcc_fsm_u0_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 0, 0, 0, 0, OSMO_GSM44068_CSTATE_U0); +} + +static void vgcs_gcc_fsm_u0_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + + switch (event) { + case VGCS_GCC_EV_SETUP_REQ: + /* The calling user initiates a new call. */ + LOG_GCC(trans, LOGL_INFO, "Received call from user.\n"); + /* Change to MM CONNECTION PENDING state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0p_MM_CONNECTION_PENDING, 0, 0); + /* Send EST-REQ to MM layer. */ + vgcs_est_req(trans); + break; + case VGCS_GCC_EV_SETUP_IND: + /* New call notification. */ + LOG_GCC(trans, LOGL_INFO, "Received call from network.\n"); + /* Change to GROUP CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0); + /* Notify call at VTY. */ + vgcs_vty_notify(trans, "Incoming call\n"); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u0p_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 0, 0, 0, 1, OSMO_GSM44068_CSTATE_U0p); + + /* Start timer */ + osmo_timer_schedule(&fi->timer, T_MM_EST, 0); +} + +static void vgcs_gcc_fsm_u0p_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + uint8_t *cause = data; + uint8_t transaction_id; + + switch (event) { + case VGCS_GCC_EV_TERM_REQ: + /* The user terminates the call. */ + LOG_GCC(trans, LOGL_INFO, "User terminates the call.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Free transaction. MM confirmation/rejection is handled without transaction also. */ + trans_free(trans); + break; + case VGCS_GCC_EV_MM_EST_REJ: + /* The MM layer rejects the call. */ + LOG_GCC(trans, LOGL_INFO, "Call was rejected by MM layer.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify reject at VTY. */ + vgcs_vty_notify(trans, "Rejected (cause %d)\n", *cause); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_MM_EST_CNF: + /* The MM connection was confirmed. */ + LOG_GCC(trans, LOGL_INFO, "Call was confirmed by MM layer.\n"); + /* Change to GROUP CALL INITIATED state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U1_GROUP_CALL_INITIATED, 0, 0); + /* Choose transaction ID. */ + transaction_id = trans_assign_trans_id(trans->ms, trans->protocol, 0); + if (transaction_id < 0) { + /* No free transaction ID. */ + trans_free(trans); + return; + } + trans->transaction_id = transaction_id; + /* Send SETUP towards network. */ + gsm44068_tx_setup(trans, trans->callref, false, 0, false, NULL, 0, false, 0); + break; + case VGCS_GCC_EV_TIMEOUT: + /* Establishment of MM layer timed out. */ + LOG_GCC(trans, LOGL_INFO, "MM layer timed out.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Free transaction. MM confirmation/rejection is handled without transaction also. */ + trans_free(trans); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u1_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 0, 0, 1, 1, OSMO_GSM44068_CSTATE_U1); +} + +static void vgcs_gcc_fsm_u1_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + uint8_t *cause = data; + + switch (event) { + case VGCS_GCC_EV_DI_CONNECT: + /* Received CONNECT from network. */ + LOG_GCC(trans, LOGL_INFO, "Call was accepted by network.\n"); + /* Change to GROUP CALL ACTIVE (separate link) state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE, 0, 0); + /* Notify connect at VTY. */ + vgcs_vty_notify(trans, "Connect\n"); + break; + case VGCS_GCC_EV_DI_TERMINATION: + /* Received TERMINATION from network. */ + LOG_GCC(trans, LOGL_INFO, "Call was terminated by network (cause %d).\n", *cause); + /* Chane to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Terminated (cause %d)\n", *cause); + /* Release MM connection. */ + vgcs_rel_req(trans); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_TERM_REQ: + /* The user terminates the call. */ + LOG_GCC(trans, LOGL_INFO, "User terminates the call.\n"); + /* Change to TERMINATION REQUESTED state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U5_TERMINATION_REQUESTED, 0, 0); + /* Send TERMINATION REQUEST towards network. */ + gsm44068_tx_termination_request(trans, trans->callref, false, 0, false, 0); + break; + case VGCS_GCC_EV_ABORT_IND: + /* Radio link was released or failed. */ + LOG_GCC(trans, LOGL_INFO, "Got release from MM layer.\n"); + /* Chane to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Released (cause %d)\n", *cause); + /* Release MM connection. */ + vgcs_rel_req(trans); + /* Free transaction. */ + trans_free(trans); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u2sl_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 1, 1, 1, 1, OSMO_GSM44068_CSTATE_U2sl_U2); +} + +static void vgcs_gcc_fsm_u2sl_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + uint8_t *cause = data; + + switch (event) { + case VGCS_GCC_EV_DI_TERMINATION: + /* Received TERMINATION from network. */ + LOG_GCC(trans, LOGL_INFO, "Call was terminated by network (cause %d).\n", *cause); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Terminated (cause %d)\n", *cause); + /* Don't release MM connection, this is done from network side using CHANNEL RELEASE. */ + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_TERM_REQ: + /* The user terminates the call. */ + LOG_GCC(trans, LOGL_INFO, "User terminates the call.\n"); + /* Change to TERMINATION REQUESTED state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U5_TERMINATION_REQUESTED, 0, 0); + /* Send TERMINATION REQUEST towards network. */ + gsm44068_tx_termination_request(trans, trans->callref, false, 0, false, 0); + break; + case VGCS_GCC_EV_ABORT_IND: + /* Radio link was released or failed. */ + LOG_GCC(trans, LOGL_INFO, "Got release from MM layer.\n"); + /* Chane to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Released (cause %d)\n", *cause); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_LISTEN_REQ: + /* The user wants to release dedicated link and join the group channel as listener. */ + LOG_GCC(trans, LOGL_INFO, "User releases uplink on dedicated channel.\n"); + /* Change state to ACTIVE (wait receive). */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE, 0, 0); + /* Set flag that we change to group receive mode after separate link. */ + trans->gcc.receive_after_sl = true; + /* Request release of the uplink. */ + gsm44068_tx_uplink_release(trans, GSM48_RR_CAUSE_LEAVE_GROUP_CA); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u2wr_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 1, 0, 1, -1, OSMO_GSM44068_CSTATE_U2wr_U6); +} + +static void vgcs_gcc_fsm_u2wr_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + uint8_t *cause = data; + + switch (event) { + case VGCS_GCC_EV_LISTEN_CNF: + /* The MM layer confirms uplink release. */ + LOG_GCC(trans, LOGL_INFO, "Uplink is now released.\n"); + /* Change to CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE, 0, 0); + /* Notify answer at VTY. */ + vgcs_vty_notify(trans, "Listening\n"); + break; + case VGCS_GCC_EV_TERM_REQ: + /* The calling subscriber wants to terminate the call. */ + LOG_GCC(trans, LOGL_INFO, "User wants to terminate. Setting termination flag.\n"); + trans->gcc.termination = true; + break; + case VGCS_GCC_EV_ABORT_REQ: + /* User aborts group channel. */ + LOG_GCC(trans, LOGL_INFO, "User leaves the group channel.\n"); + /* Change to GROUP CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0); + /* Reset flag after we changed to group receive mode after separate link. */ + trans->gcc.receive_after_sl = false; + /* Notify leaving at VTY. */ + vgcs_vty_notify(trans, "Call left\n"); + /* Release MM connection. (Leave call.) */ + vgcs_rel_req(trans); + break; + case VGCS_GCC_EV_ABORT_IND: + /* The MM layer released the group channel. */ + LOG_GCC(trans, LOGL_INFO, "Release/failure received from MM layer.\n"); + if (trans->gcc.receive_after_sl) { + LOG_GCC(trans, LOGL_INFO, "Ignoring release, because we released separate link.\n"); + break; + } + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Released (cause %d)\n", *cause); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_MM_IDLE: + if (!trans->gcc.receive_after_sl) + break; + /* If no channel is available (no notification received), enter the U2nc state. */ + if (!trans->gcc.ch_desc_present) { + /* Change state to ACTIVE (no channel). */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE, 0, 0); + /* Notify answer at VTY. */ + vgcs_vty_notify(trans, "Listen (no channel yet)\n"); + break; + } + /* The MM layer indicates that the phone is ready to request group channel. */ + LOG_GCC(trans, LOGL_INFO, "MM is now idle, we can request group channel.\n"); + /* Send GROUP-REQ to MM layer. */ + vgcs_group_req(trans); + break; + case VGCS_GCC_EV_JOIN_GC_CNF: + /* Reset flag after we changed to group receive mode after separate link. */ + trans->gcc.receive_after_sl = false; + /* The MM layer confirms group channel. */ + LOG_GCC(trans, LOGL_INFO, "Joined group call after releasing separate link.\n"); + /* Change to CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE, 0, 0); + /* Notify answer at VTY. */ + vgcs_vty_notify(trans, "Listening\n"); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u2r_u6_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 1, 0, 0, -1, (trans->protocol == GSM48_PDISC_GROUP_CC) ? + OSMO_GSM44068_CSTATE_U2r : OSMO_GSM44068_CSTATE_U2wr_U6); + + /* There is a pending termination request, request uplink. */ + if (trans->gcc.termination) + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_TALK_REQ, NULL); +} + +static void vgcs_gcc_fsm_u2r_u6_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + uint8_t *cause = data; + + switch (event) { + case VGCS_GCC_EV_TERM_REQ: + /* The calling subscriber wants to terminate the call. */ + LOG_GCC(trans, LOGL_INFO, "User wants to terminate. Setting termination flag.\n"); + trans->gcc.termination = true; + /* fall-thru */ + case VGCS_GCC_EV_TALK_REQ: + /* The user wants to talk. */ + LOG_GCC(trans, LOGL_INFO, "User wants to talk on the uplink.\n"); + /* Change to GROUP CALL ACTIVE (wait send) state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2ws_GROUP_CALL_ACTIVE, 0, 0); + /* Request group transmit mode from MM layer. */ + vgcs_uplink_req(trans); + break; + case VGCS_GCC_EV_ABORT_REQ: + /* User aborts group channel. */ + LOG_GCC(trans, LOGL_INFO, "User leaves the group channel.\n"); + /* Change to GROUP CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0); + /* Notify leaving at VTY. */ + vgcs_vty_notify(trans, "Call left\n"); + /* Release MM connection. (Leave call.) */ + vgcs_rel_req(trans); + break; + case VGCS_GCC_EV_ABORT_IND: + /* The MM layer released the group channel. */ + LOG_GCC(trans, LOGL_INFO, "Release/failure received from MM layer.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Released (cause %d)\n", *cause); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_UPLINK_FREE: + /* The MM layer indicates that the uplink is free. */ + LOG_GCC(trans, LOGL_INFO, "Uplink free indication received from MM layer.\n"); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Uplink free => You may talk\n"); + break; + case VGCS_GCC_EV_UPLINK_BUSY: + /* The MM layer indicates that the uplink is busy. */ + LOG_GCC(trans, LOGL_INFO, "Uplink busy indication received from MM layer.\n"); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Uplink busy => You cannot talk\n"); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u2ws_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 1, 1, 0, -1, OSMO_GSM44068_CSTATE_U2ws); +} + +static void vgcs_gcc_fsm_u2ws_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + uint8_t *cause = data; + + switch (event) { + case VGCS_GCC_EV_TALK_CNF: + /* Uplink was granted. */ + LOG_GCC(trans, LOGL_INFO, "Uplink established, user can talk now.\n"); + /* Change to GROUP CALL ACTIVE (send and receive) state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2sr_GROUP_CALL_ACTIVE, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Talking\n"); + break; + case VGCS_GCC_EV_TALK_REJ: + /* Uplink was rejected. */ + LOG_GCC(trans, LOGL_INFO, "Uplink rejected, user cannot talk.\n"); + /* Clear termination flag, if set. */ + trans->gcc.termination = false; + /* Change to GROUP CALL ACTIVE (receive) state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Talking rejected (cause %d)\n", *cause); + break; + case VGCS_GCC_EV_TERM_REQ: + /* The calling subscriber wants to terminate the call. */ + LOG_GCC(trans, LOGL_INFO, "User wants to terminate. Setting termination flag.\n"); + trans->gcc.termination = true; + break; + case VGCS_GCC_EV_ABORT_REQ: + /* User aborts group channel. */ + LOG_GCC(trans, LOGL_INFO, "User leaves the group channel.\n"); + /* Change to GROUP CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0); + /* Notify leaving at VTY. */ + vgcs_vty_notify(trans, "Call left\n"); + /* Release MM connection. (Leave call.) */ + vgcs_rel_req(trans); + break; + case VGCS_GCC_EV_ABORT_IND: + /* The MM layer released the group channel. */ + LOG_GCC(trans, LOGL_INFO, "Release/failure received from MM layer.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Released (cause %d)\n", *cause); + /* Free transaction. */ + trans_free(trans); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u2sr_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 1, 1, 1, -1, OSMO_GSM44068_CSTATE_U2sr); + + /* There is a pending termination request, request uplink. */ + if (trans->gcc.termination) + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_TERM_REQ, NULL); +} + +static void vgcs_gcc_fsm_u2sr_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + uint8_t *cause = data; + + switch (event) { + case VGCS_GCC_EV_DI_TERMINATION: + /* Received TERMINATION from network. */ + LOG_GCC(trans, LOGL_INFO, "Call was terminated by network (cause %d).\n", *cause); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Terminated (cause %d)\n", *cause); + /* Don't release MM connection, this is done from network side using CHANNEL RELEASE. */ + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_TALK_REJ: + /* Uplink was rejected by network. (Reject after granting access.) */ + LOG_GCC(trans, LOGL_INFO, "Uplink rejected, user cannot talk.\n"); + /* Change to GROUP CALL ACTIVE (receive) state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Talking rejected (cause %d)\n", *cause); + break; + case VGCS_GCC_EV_LISTEN_REQ: + /* The user wants to release the uplink and become a listener. */ + LOG_GCC(trans, LOGL_INFO, "User wants to release the uplink.\n"); + /* Change to GROUP CALL ACTIVE (wait receive) state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE, 0, 0); + /* Request group receive mode from MM layer. */ + vgcs_uplink_rel_req(trans); + break; + case VGCS_GCC_EV_TERM_REQ: + /* The user terminates the call. */ + LOG_GCC(trans, LOGL_INFO, "User terminates the call.\n"); + /* Change to TERMINATION REQUESTED state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U5_TERMINATION_REQUESTED, 0, 0); + /* Send TERMINATION REQUEST towards network. */ + gsm44068_tx_termination_request(trans, trans->callref, false, 0, false, 0); + break; + case VGCS_GCC_EV_ABORT_REQ: + /* User aborts group channel. */ + LOG_GCC(trans, LOGL_INFO, "User leaves the group channel.\n"); + /* Change to GROUP CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0); + /* Notify leaving at VTY. */ + vgcs_vty_notify(trans, "Call left\n"); + /* Release MM connection. (Leave call.) */ + vgcs_rel_req(trans); + break; + case VGCS_GCC_EV_ABORT_IND: + /* The MM layer released the group channel. */ + LOG_GCC(trans, LOGL_INFO, "Release/failure received from MM layer.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Released (cause %d)\n", *cause); + /* Free transaction. */ + trans_free(trans); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u2nc_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 1, 1, 0, -1, OSMO_GSM44068_CSTATE_U2nc); + + /* Start timer */ + osmo_timer_schedule(&fi->timer, T_NO_CHANNEL, 0); +} + +static void vgcs_gcc_fsm_u2nc_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + + switch (event) { + case VGCS_GCC_EV_SETUP_IND: + /* Channel becomes available, now join the group call. */ + LOG_GCC(trans, LOGL_INFO, "Radio channel becomes available.\n"); + /* Change to CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST, 0, 0); + /* Send GROUP-REQ to MM layer. */ + vgcs_group_req(trans); + break; + case VGCS_GCC_EV_TERM_REQ: + /* The calling subscriber wants to terminate the call. */ + LOG_GCC(trans, LOGL_INFO, "User wants to terminate. Setting termination flag.\n"); + trans->gcc.termination = true; + break; + case VGCS_GCC_EV_ABORT_REQ: + /* User aborts group channel. */ + LOG_GCC(trans, LOGL_INFO, "User leaves the group channel, that is not yet established.\n"); + /* Change to GROUP CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0); + break; + case VGCS_GCC_EV_ABORT_IND: + /* The MM layer indicates that group channel is gone. */ + LOG_GCC(trans, LOGL_INFO, "Group call notification is gone.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Released\n"); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_TIMEOUT: + /* Group channel did not become availblet. */ + LOG_GCC(trans, LOGL_INFO, "Timeout waiting for group channel.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Timeout\n"); + /* Free transaction. */ + trans_free(trans); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u3_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 0, 0, 0, 0, OSMO_GSM44068_CSTATE_U3); +} + +static void vgcs_gcc_fsm_u3_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + + switch (event) { + case VGCS_GCC_EV_JOIN_GC_REQ: + /* Join (answer) incoming group call. */ + LOG_GCC(trans, LOGL_INFO, "Join call.\n"); + /* If no channel is available, enter the U2nc state. */ + if (!trans->gcc.ch_desc_present) { + /* Change state to ACTIVE (no channel). */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE, 0, 0); + /* Notify answer at VTY. */ + vgcs_vty_notify(trans, "Answer (no channel yet)\n"); + break; + } + /* Change to CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST, 0, 0); + /* Send GROUP-REQ to MM layer. */ + vgcs_group_req(trans); + break; + case VGCS_GCC_EV_ABORT_REQ: + /* User rejects group call. */ + LOG_GCC(trans, LOGL_INFO, "User rejects group call.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_ABORT_IND: + /* The notified call is gone. */ + LOG_GCC(trans, LOGL_INFO, "Received call from network is gone.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. (No cause, because notification is gone.) */ + vgcs_vty_notify(trans, "Released\n"); + /* Free transaction. */ + trans_free(trans); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u4_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 0, 0, 0, 0, OSMO_GSM44068_CSTATE_U4); + + /* Start timer */ + osmo_timer_schedule(&fi->timer, T_CONN_REQ, 0); +} + +static void vgcs_gcc_fsm_u4_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + + switch (event) { + case VGCS_GCC_EV_JOIN_GC_CNF: + /* The MM layer confirms the group receive mode. (We have a channel.) */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE, 0, 0); + /* Notify answer at VTY. */ + vgcs_vty_notify(trans, "Answer\n"); + break; + case VGCS_GCC_EV_ABORT_REQ: + /* User aborts group channel. */ + LOG_GCC(trans, LOGL_INFO, "User aborts group channel.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_ABORT_IND: + /* The notified call is gone. */ + LOG_GCC(trans, LOGL_INFO, "Received call from network is gone.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. (No cause, because notification is gone.) */ + vgcs_vty_notify(trans, "Released\n"); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_TIMEOUT: + /* Group channel timed out. */ + LOG_GCC(trans, LOGL_INFO, "Timeout waiting for group channel.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Timeout\n"); + /* Free transaction. */ + trans_free(trans); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u5_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, -1, -1, 1, 1, OSMO_GSM44068_CSTATE_U5); + + /* Start timer */ + osmo_timer_schedule(&fi->timer, T_TERM, 0); +} + +static void vgcs_gcc_fsm_u5_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + uint8_t *cause = data; + + switch (event) { + case VGCS_GCC_EV_DI_TERMINATION: + /* The network confirm the termination. */ + LOG_GCC(trans, LOGL_INFO, "Termination confirmend by network.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Terminated (cause %d)\n", *cause); + /* Don't release MM connection, this is done from network side using CHANNEL RELEASE. */ + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_DI_TERM_REJECT: + /* Termination was rejected. */ + LOG_GCC(trans, LOGL_INFO, "Termination rejected (cause %d), releasing MM connection.\n", *cause); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Termination rejected (cause %d), Call left\n", *cause); + /* Release MM connection. (Leave call.) + * We must release here, because we can have a dedicated MM connection or joined a group channel. + * We cannot go back, because we don't know what state we had before U5 state was entered. + * Do we really need to go back or just leave the call? */ + vgcs_rel_req(trans); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_ABORT_IND: + /* The notified call is gone. */ + LOG_GCC(trans, LOGL_INFO, "Received call from network is gone.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Released (cause %d)\n", *cause); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_TIMEOUT: + /* Termination timed out. */ + LOG_GCC(trans, LOGL_INFO, "Timeout waiting for termination.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Timeout\n"); + /* Release MM connection. (Leave call.) */ + vgcs_rel_req(trans); + /* Free transaction. */ + trans_free(trans); + break; + default: + OSMO_ASSERT(0); + } +} + +static int vgcs_gcc_fsm_timer_cb(struct osmo_fsm_inst *fi) +{ + return osmo_fsm_inst_dispatch(fi, VGCS_GCC_EV_TIMEOUT, NULL); +} + +static const struct osmo_fsm_state vgcs_gcc_fsm_states[] = { + [VGCS_GCC_ST_U0_NULL] = { + .name = "NULL (U0)", + .in_event_mask = S(VGCS_GCC_EV_SETUP_REQ) | + S(VGCS_GCC_EV_SETUP_IND), + .out_state_mask = S(VGCS_GCC_ST_U0p_MM_CONNECTION_PENDING) | + S(VGCS_GCC_ST_U1_GROUP_CALL_INITIATED) | + S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT), + .onenter = vgcs_gcc_fsm_u0_onenter, + .action = vgcs_gcc_fsm_u0_action, + }, + [VGCS_GCC_ST_U0p_MM_CONNECTION_PENDING] = { + .name = "MM CONNECTION PENDING (U0.p)", + .in_event_mask = S(VGCS_GCC_EV_TERM_REQ) | + S(VGCS_GCC_EV_MM_EST_REJ) | + S(VGCS_GCC_EV_MM_EST_CNF) | + S(VGCS_GCC_EV_TIMEOUT), + .out_state_mask = S(VGCS_GCC_ST_U0_NULL) | + S(VGCS_GCC_ST_U1_GROUP_CALL_INITIATED), + .onenter = vgcs_gcc_fsm_u0p_onenter, + .action = vgcs_gcc_fsm_u0p_action, + }, + [VGCS_GCC_ST_U1_GROUP_CALL_INITIATED] = { + .name = "GROUP CALL INITIATED (U1)", + .in_event_mask = S(VGCS_GCC_EV_DI_CONNECT) | + S(VGCS_GCC_EV_DI_TERMINATION) | + S(VGCS_GCC_EV_TERM_REQ) | + S(VGCS_GCC_EV_ABORT_IND), + .out_state_mask = S(VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U5_TERMINATION_REQUESTED) | + S(VGCS_GCC_ST_U0_NULL), + .onenter = vgcs_gcc_fsm_u1_onenter, + .action = vgcs_gcc_fsm_u1_action, + }, + [VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE] = { + .name = "GROUP CALL ACTIVE separate link (U2sl)", + .in_event_mask = S(VGCS_GCC_EV_DI_TERMINATION) | + S(VGCS_GCC_EV_TERM_REQ) | + S(VGCS_GCC_EV_ABORT_IND) | + S(VGCS_GCC_EV_LISTEN_REQ), + .out_state_mask = S(VGCS_GCC_ST_U0_NULL) | + S(VGCS_GCC_ST_U5_TERMINATION_REQUESTED) | + S(VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE), + .onenter = vgcs_gcc_fsm_u2sl_onenter, + .action = vgcs_gcc_fsm_u2sl_action, + }, + [VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE] = { + .name = "GROUP CALL ACTIVE wait for receive mode (U2wr)", + .in_event_mask = S(VGCS_GCC_EV_LISTEN_CNF) | + S(VGCS_GCC_EV_TERM_REQ) | + S(VGCS_GCC_EV_ABORT_REQ) | + S(VGCS_GCC_EV_ABORT_IND) | + S(VGCS_GCC_EV_MM_IDLE) | + S(VGCS_GCC_EV_JOIN_GC_CNF), + .out_state_mask = S(VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) | + S(VGCS_GCC_ST_U0_NULL), + .onenter = vgcs_gcc_fsm_u2wr_onenter, + .action = vgcs_gcc_fsm_u2wr_action, + }, + [VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE] = { + .name = "GROUP CALL ACTIVE receive mode (U2r or U6 @ BCC)", + .in_event_mask = S(VGCS_GCC_EV_TALK_REQ) | + S(VGCS_GCC_EV_TERM_REQ) | + S(VGCS_GCC_EV_ABORT_REQ) | + S(VGCS_GCC_EV_ABORT_IND) | + S(VGCS_GCC_EV_UPLINK_FREE) | + S(VGCS_GCC_EV_UPLINK_BUSY), + .out_state_mask = S(VGCS_GCC_ST_U2ws_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) | + S(VGCS_GCC_ST_U0_NULL), + .onenter = vgcs_gcc_fsm_u2r_u6_onenter, + .action = vgcs_gcc_fsm_u2r_u6_action, + }, + [VGCS_GCC_ST_U2ws_GROUP_CALL_ACTIVE] = { + .name = "GROUP CALL ACTIVE wait for send+receive mode (U2ws)", + .in_event_mask = S(VGCS_GCC_EV_TALK_CNF) | + S(VGCS_GCC_EV_TALK_REJ) | + S(VGCS_GCC_EV_TERM_REQ) | + S(VGCS_GCC_EV_ABORT_REQ) | + S(VGCS_GCC_EV_ABORT_IND), + .out_state_mask = S(VGCS_GCC_ST_U2sr_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) | + S(VGCS_GCC_ST_U0_NULL), + .onenter = vgcs_gcc_fsm_u2ws_onenter, + .action = vgcs_gcc_fsm_u2ws_action, + }, + [VGCS_GCC_ST_U2sr_GROUP_CALL_ACTIVE] = { + .name = "GROUP CALL ACTIVE send+receive mode (U2sr)", + .in_event_mask = S(VGCS_GCC_EV_DI_TERMINATION) | + S(VGCS_GCC_EV_LISTEN_REQ) | + S(VGCS_GCC_EV_TALK_REJ) | + S(VGCS_GCC_EV_TERM_REQ) | + S(VGCS_GCC_EV_ABORT_REQ) | + S(VGCS_GCC_EV_ABORT_IND), + .out_state_mask = S(VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) | + S(VGCS_GCC_ST_U0_NULL), + .onenter = vgcs_gcc_fsm_u2sr_onenter, + .action = vgcs_gcc_fsm_u2sr_action, + }, + [VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE] = { + .name = "GROUP CALL ACTIVE no channel (U2nc)", + .in_event_mask = S(VGCS_GCC_EV_SETUP_IND) | + S(VGCS_GCC_EV_TERM_REQ) | + S(VGCS_GCC_EV_ABORT_REQ) | + S(VGCS_GCC_EV_ABORT_IND) | + S(VGCS_GCC_EV_TIMEOUT), + .out_state_mask = S(VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST) | + S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) | + S(VGCS_GCC_ST_U0_NULL), + .onenter = vgcs_gcc_fsm_u2nc_onenter, + .action = vgcs_gcc_fsm_u2nc_action, + }, + [VGCS_GCC_ST_U3_GROUP_CALL_PRESENT] = { + .name = "GROUP CALL PRESENT (U3)", + .in_event_mask = S(VGCS_GCC_EV_JOIN_GC_REQ) | + S(VGCS_GCC_EV_ABORT_REQ) | + S(VGCS_GCC_EV_ABORT_IND), + .out_state_mask = S(VGCS_GCC_ST_U0_NULL) | + S(VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST) | + S(VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE), + .onenter = vgcs_gcc_fsm_u3_onenter, + .action = vgcs_gcc_fsm_u3_action, + }, + [VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST] = { + .name = "GROUP CALL CONNECTION REQUEST (U4)", + .in_event_mask = S(VGCS_GCC_EV_JOIN_GC_CNF) | + S(VGCS_GCC_EV_ABORT_REQ) | + S(VGCS_GCC_EV_ABORT_IND) | + S(VGCS_GCC_EV_TIMEOUT), + .out_state_mask = S(VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U0_NULL), + .onenter = vgcs_gcc_fsm_u4_onenter, + .action = vgcs_gcc_fsm_u4_action, + }, + [VGCS_GCC_ST_U5_TERMINATION_REQUESTED] = { + .name = "TERMINATION REQUESTED (U5)", + .in_event_mask = S(VGCS_GCC_EV_DI_TERMINATION) | + S(VGCS_GCC_EV_DI_TERM_REJECT) | + S(VGCS_GCC_EV_ABORT_IND) | + S(VGCS_GCC_EV_TIMEOUT), + .out_state_mask = S(VGCS_GCC_ST_U0_NULL), + .onenter = vgcs_gcc_fsm_u5_onenter, + .action = vgcs_gcc_fsm_u5_action, + }, +}; + +static struct osmo_fsm vgcs_gcc_fsm = { + .name = "gcc", + .states = vgcs_gcc_fsm_states, + .num_states = ARRAY_SIZE(vgcs_gcc_fsm_states), + .log_subsys = DGCC, + .event_names = vgcs_gcc_fsm_event_names, + .timer_cb = vgcs_gcc_fsm_timer_cb, +}; + +static struct osmo_fsm vgcs_bcc_fsm = { + .name = "bcc", + .states = vgcs_gcc_fsm_states, + .num_states = ARRAY_SIZE(vgcs_gcc_fsm_states), + .log_subsys = DBCC, + .event_names = vgcs_gcc_fsm_event_names, + .timer_cb = vgcs_gcc_fsm_timer_cb, +}; + +static const char *gsm44068_gcc_state_name(struct osmo_fsm_inst *fi) +{ + return vgcs_gcc_fsm_states[fi->state].name; +} + +/* + * transaction + */ + +/* Create transaction together with state machine and set initail states. */ +static struct gsm_trans *trans_alloc_vgcs(struct osmocom_ms *ms, uint8_t pdisc, uint8_t transaction_id, + uint32_t callref) +{ + struct gsm_trans *trans; + + trans = trans_alloc(ms, pdisc, 0xff, callref); + if (trans < 0) + return NULL; + trans->gcc.fi = osmo_fsm_inst_alloc((pdisc == GSM48_PDISC_GROUP_CC) ? &vgcs_gcc_fsm : &vgcs_bcc_fsm, + trans, trans, LOGL_DEBUG, NULL); + if (!trans->gcc.fi) { + trans_free(trans); + return NULL; + } + + LOG_GCC(trans, LOGL_DEBUG, "Created transaction for callref %d, pdisc %d, transaction_id 0x%x\n", + callref, pdisc, transaction_id); + + set_state_attributes(trans, 0, 0, 0, 0, OSMO_GSM44068_CSTATE_U0); + + return trans; +} + +/* Call Control Specific transaction release. + * gets called by trans_free, DO NOT CALL YOURSELF! + */ +void _gsm44068_gcc_bcc_trans_free(struct gsm_trans *trans) +{ + if (!trans->gcc.fi) + return; + + /* Free state machine. */ + osmo_fsm_inst_free(trans->gcc.fi); +} + +/* Find ongoing call. (The call must not be present.) */ +struct gsm_trans *trans_find_ongoing_gcc_bcc(struct osmocom_ms *ms) +{ + struct gsm_trans *trans; + + llist_for_each_entry(trans, &ms->trans_list, entry) { + if (trans->protocol != GSM48_PDISC_GROUP_CC && trans->protocol != GSM48_PDISC_BCAST_CC) + continue; + if (trans->gcc.fi->state != VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) + return trans; + } + + return NULL; +} + +/* + * messages from upper/lower layers + */ + +static int gsm44068_gcc_data_ind(struct gsm_trans *trans, struct msgb *msg, uint8_t transaction_id) +{ + struct osmocom_ms *ms = trans->ms; + struct gsm48_hdr *gh = msgb_l3(msg); + int msg_type = gh->msg_type & 0xbf; + int rc = 0; + uint8_t cause = 0; + uint8_t d_att, u_att, comm, orig; + + /* pull the MMCC header */ + msgb_pull(msg, sizeof(struct gsm48_mmxx_hdr)); + + /* Use transaction ID from message. If we join a group call, we don't have it until now. */ + trans->transaction_id = transaction_id; + + LOG_GCC(trans, LOGL_INFO, "(ms %s) Received '%s' in state %s\n", ms->name, gsm44068_gcc_msg_name(msg_type), + gsm44068_gcc_state_name(trans->gcc.fi)); + + switch (msg_type) { + case OSMO_GSM44068_MSGT_CONNECT: + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_DI_CONNECT, NULL); + break; + case OSMO_GSM44068_MSGT_TERMINATION: + rc = gsm44068_rx_termination(trans, msg, &cause, NULL, NULL); + if (rc < 0) + break; + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_DI_TERMINATION, &cause); + break; + case OSMO_GSM44068_MSGT_TERMINATION_REJECT: + rc = gsm44068_rx_termination(trans, msg, &cause, NULL, NULL); + if (rc < 0) + break; + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_DI_TERM_REJECT, &cause); + break; + case OSMO_GSM44068_MSGT_GET_STATUS: + /* Note: The message via UNIT DATA is not supported. */ + gsm44068_tx_status(trans, OSMO_GSM44068_CAUSE_RESPONSE_TO_GET_STATUS, NULL, 0, + true, trans->gcc.call_state, + true, trans->gcc.d_att, trans->gcc.u_att, trans->gcc.comm, trans->gcc.orig); + break; + case OSMO_GSM44068_MSGT_SET_PARAMETER: + rc = gsm44068_rx_set_parameter(trans, msg, &d_att, &u_att, &comm, &orig); + if (rc >= 0) + set_state_attributes(trans, d_att, u_att, comm, orig, -1); + break; + default: + LOG_GCC(trans, LOGL_NOTICE, "Message '%s' not supported.\n", gsm44068_gcc_msg_name(msg_type)); + rc = -EINVAL; + } + + return rc; +} + +/* receive message from MM layer */ +int gsm44068_rcv_gcc_bcc(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_settings *set = &ms->settings; + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + int msg_type = mmh->msg_type; + uint8_t pdisc; + struct gsm_trans *trans; + int rc = 0; + uint8_t cause; + + /* Check for message class and get protocol type. */ + switch ((msg_type & GSM48_MMXX_MASK)) { + case GSM48_MMGCC_CLASS: + pdisc = GSM48_PDISC_GROUP_CC; + if (!set->vgcs) { + LOGP(DGCC, LOGL_ERROR, "Ignoring message '%s', because VGCS is not supported!\n", + get_mmxx_name(msg_type)); + return -ENOTSUP; + } + break; + case GSM48_MMBCC_CLASS: + pdisc = GSM48_PDISC_BCAST_CC; + if (!set->vbs) { + LOGP(DGCC, LOGL_ERROR, "Ignoring message '%s', because VBS is not supported!\n", + get_mmxx_name(msg_type)); + return -ENOTSUP; + } + break; + default: + LOGP(DGCC, LOGL_ERROR, "Message class not allowed, please fix!\n"); + return -EINVAL; + } + + trans = trans_find_by_callref(ms, pdisc, mmh->ref); + if (!trans) { + LOG_GCC_PR(pdisc, mmh->ref, LOGL_INFO, "(ms %s) Received '%s' without transaction.\n", + ms->name, get_mmxx_name(msg_type)); + + if (msg_type == GSM48_MMGCC_REL_IND || msg_type == GSM48_MMBCC_REL_IND) { + /* If we got the MMxx-EST-REJ after we aborted the call, we ignore. */ + return 0; + } + if (msg_type == GSM48_MMGCC_EST_CNF || msg_type == GSM48_MMBCC_EST_CNF || + msg_type == GSM48_MMGCC_GROUP_CNF || msg_type == GSM48_MMBCC_GROUP_CNF) { + struct msgb *nmsg; + LOG_GCC_PR(pdisc, mmh->ref, LOGL_ERROR, "Received confirm for GCC/BCC call, " + "but there is no transaction, releasing.\n"); + /* If we got the MMxx-EST-CNF after we aborted the call, we release. */ + msg_type = (pdisc == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_REL_REQ : GSM48_MMBCC_REL_REQ; + nmsg = gsm48_mmxx_msgb_alloc(msg_type, mmh->ref, mmh->transaction_id, 0); + if (!nmsg) + return -ENOMEM; + LOG_GCC_PR(pdisc, mmh->ref, LOGL_INFO, "Sending %s\n", get_mmxx_name(msg_type)); + gsm48_mmxx_downmsg(ms, nmsg); + return 0; + + } else if (msg_type == GSM48_MMGCC_NOTIF_IND || msg_type == GSM48_MMBCC_NOTIF_IND) { + /* Notification gone but no transaction. */ + if (mmh->notify != MMXX_NOTIFY_SETUP) + return 0; + /* Incoming notification, creation transaction. */ + trans = trans_alloc_vgcs(ms, pdisc, 0xff, mmh->ref); + if (trans < 0) + return -ENOMEM; + } else { + LOG_GCC_PR(pdisc, mmh->ref, LOGL_ERROR, "Received GCC/BCC message for unknown transaction.\n"); + return -ENOENT; + } + } + + LOG_GCC(trans, LOGL_INFO, "(ms %s) Received '%s' in state %s\n", ms->name, get_mmxx_name(msg_type), + gsm44068_gcc_state_name(trans->gcc.fi)); + + switch (msg_type) { + case GSM48_MMGCC_EST_CNF: + case GSM48_MMBCC_EST_CNF: + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_MM_EST_CNF, NULL); + break; + case GSM48_MMGCC_ERR_IND: + case GSM48_MMBCC_ERR_IND: + case GSM48_MMGCC_REL_IND: + case GSM48_MMBCC_REL_IND: + /* If MM fails or is rejected during U0.p state, this is a MM-EST-REJ. */ + if (trans->gcc.fi->state == VGCS_GCC_ST_U0p_MM_CONNECTION_PENDING) + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_MM_EST_REJ, &mmh->cause); + else + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_ABORT_IND, &mmh->cause); + break; + case GSM48_MMGCC_DATA_IND: + case GSM48_MMBCC_DATA_IND: + rc = gsm44068_gcc_data_ind(trans, msg, mmh->transaction_id); + break; + case GSM48_MMGCC_NOTIF_IND: + case GSM48_MMBCC_NOTIF_IND: + switch (mmh->notify) { + case MMXX_NOTIFY_SETUP: + /* Store channel description. */ + trans->gcc.ch_desc_present = mmh->ch_desc_present; + memcpy(&trans->gcc.ch_desc, &mmh->ch_desc, sizeof(trans->gcc.ch_desc)); + if (trans->gcc.fi->state == VGCS_GCC_ST_U0_NULL) { + /* In null state this indication is a SETUP-IND. */ + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_SETUP_IND, NULL); + } else if (trans->gcc.fi->state == VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE && mmh->ch_desc_present) { + /* In U2nc state with a channel info, is a SETUP-IND (with updated mode). */ + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_SETUP_IND, NULL); + } + break; + case MMXX_NOTIFY_RELEASE: + if (trans->gcc.fi->state == VGCS_GCC_ST_U3_GROUP_CALL_PRESENT || + trans->gcc.fi->state == VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST || + trans->gcc.fi->state == VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE) { + /* If notification is gone, abort pending received call. */ + cause = GSM48_CC_CAUSE_NORM_CALL_CLEAR; + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_ABORT_IND, &cause); + } + break; + } + break; + case GSM48_MMGCC_GROUP_CNF: + case GSM48_MMBCC_GROUP_CNF: + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_JOIN_GC_CNF, NULL); + break; + case GSM48_MMGCC_UPLINK_CNF: + case GSM48_MMBCC_UPLINK_CNF: + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_TALK_CNF, NULL); + break; + case GSM48_MMGCC_UPLINK_REL_IND: + case GSM48_MMBCC_UPLINK_REL_IND: + if (trans->gcc.fi->state == VGCS_GCC_ST_U2ws_GROUP_CALL_ACTIVE || + trans->gcc.fi->state == VGCS_GCC_ST_U2sr_GROUP_CALL_ACTIVE) { + /* We are waiting to send, so this is a reject. */ + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_TALK_REJ, &mmh->cause); + } else if (trans->gcc.fi->state == VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE) { + /* We are waiting to receive, so this is a confirm. */ + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_LISTEN_CNF, &mmh->cause); + } + break; + case GSM48_MMGCC_UPLINK_FREE_IND: + case GSM48_MMBCC_UPLINK_FREE_IND: + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_UPLINK_FREE, NULL); + break; + case GSM48_MMGCC_UPLINK_BUSY_IND: + case GSM48_MMBCC_UPLINK_BUSY_IND: + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_UPLINK_BUSY, NULL); + break; + default: + LOG_GCC(trans, LOGL_NOTICE, "Message '%s' unhandled.\n", get_mmxx_name(msg_type)); + rc = -ENOTSUP; + } + + return rc; +} + +/* Special function to receive IDLE state of MM layer. This is required to request a channel after dedicated mode. */ +int gsm44068_rcv_mm_idle(struct osmocom_ms *ms) +{ + struct gsm_trans *trans; + + trans = trans_find_ongoing_gcc_bcc(ms); + if (!trans) + return -ENOENT; + if (trans->gcc.fi->state == VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE && trans->gcc.receive_after_sl) { + /* Finally the MM layer is IDLE. */ + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_MM_IDLE, NULL); + return 0; + } + return -EINVAL; +} + +/* Setup or join a VGC/VBC. */ +int gcc_bcc_call(struct osmocom_ms *ms, uint8_t protocol, const char *number) +{ + uint32_t callref = strtoul(number, NULL, 0); + struct gsm_trans *trans; + int i; + + if (strlen(number) > 8) { +inval: + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Invalid group '%s'\n", number); + return -EINVAL; + } + + for (i = 0; i < strlen(number); i++) { + if (number[i] < '0' || number[i] > '9') + goto inval; + } + + if (callref == 0) + goto inval; + + /* Reject if there is already an ongoing call. */ + trans = trans_find_ongoing_gcc_bcc(ms); + if (trans) { + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Cannot call/join, we are busy\n"); + return -EBUSY; + } + + /* Find call that matches the given protocol+cellref. */ + trans = trans_find_by_callref(ms, protocol, callref); + if (trans) { + /* Answer incoming call. */ + if (trans->gcc.fi->state == VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) { + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_JOIN_GC_REQ, NULL); + return 0; + } + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Call already established\n"); + return -EEXIST; + } + + /* Create new transaction. ORIG will be set when entering U2sl state. */ + trans = trans_alloc_vgcs(ms, protocol, 0xff, callref); + if (trans < 0) + return -ENOMEM; + + /* Setup new call. */ + return osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_SETUP_REQ, NULL); +} + +/* Leave a VGC. (If we are the originator, we do not terminate.) */ +int gcc_leave(struct osmocom_ms *ms) +{ + struct gsm_trans *trans; + + /* Reject if there is no call. */ + trans = trans_find_ongoing_gcc_bcc(ms); + if (!trans) { + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "No Call\n"); + return -EINVAL; + } + + if (trans->gcc.fi->state == VGCS_GCC_ST_U0p_MM_CONNECTION_PENDING || + trans->gcc.fi->state == VGCS_GCC_ST_U1_GROUP_CALL_INITIATED || + trans->gcc.fi->state == VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE || + trans->gcc.fi->state == VGCS_GCC_ST_U5_TERMINATION_REQUESTED) { + LOG_GCC(trans, LOGL_NOTICE, "Cannot leave (abort), in this state.\n"); + return -EINVAL; + } + + /* Send ABORT-REQ to leave the call. */ + return osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_ABORT_REQ, NULL); +} + +/* Hangup VGC/VBC. (If we are the originator, we terminate the call.) */ +int gcc_bcc_hangup(struct osmocom_ms *ms) +{ + struct gsm_trans *trans; + + /* Reject if there is no call. */ + trans = trans_find_ongoing_gcc_bcc(ms); + if (!trans) { + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "No Call\n"); + return -EINVAL; + } + + if (trans->gcc.orig) { + if (trans->gcc.fi->state == VGCS_GCC_ST_U3_GROUP_CALL_PRESENT || + trans->gcc.fi->state == VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST || + trans->gcc.fi->state == VGCS_GCC_ST_U5_TERMINATION_REQUESTED) { + LOG_GCC(trans, LOGL_NOTICE, "Cannot hangup (terminate) in this state.\n"); + return -EINVAL; + } + /* Send TERM-REQ to leave the call. */ + return osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_TERM_REQ, NULL); + } + + if (trans->gcc.fi->state == VGCS_GCC_ST_U5_TERMINATION_REQUESTED) { + LOG_GCC(trans, LOGL_NOTICE, "Cannot hangup (abort) in this state.\n"); + return -EINVAL; + } + /* Send ABORT-REQ to leave the call. */ + return osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_ABORT_REQ, NULL); +} + +int gcc_talk(struct osmocom_ms *ms) +{ + struct gsm_trans *trans; + + /* Reject if there is no call. */ + trans = trans_find_ongoing_gcc_bcc(ms); + if (!trans) { + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "No Call\n"); + return -EINVAL; + } + + if (trans->protocol != GSM48_PDISC_GROUP_CC) { + LOG_GCC(trans, LOGL_NOTICE, "Cannot talk, the ongoing call is not a group call.\n"); + vgcs_vty_notify(trans, "Not a group call\n"); + return -EIO; + } + + if (trans->gcc.fi->state != VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE) { + LOG_GCC(trans, LOGL_NOTICE, "Cannot talk, we are not listening.\n"); + return -EINVAL; + } + + /* Send TALK-REQ to become talker. */ + return osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_TALK_REQ, NULL); +} + +int gcc_listen(struct osmocom_ms *ms) +{ + struct gsm_trans *trans; + + /* Reject if there is no call. */ + trans = trans_find_ongoing_gcc_bcc(ms); + if (!trans) { + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "No Call\n"); + return -EINVAL; + } + + if (trans->protocol != GSM48_PDISC_GROUP_CC) { + LOG_GCC(trans, LOGL_NOTICE, "Cannot become listener, the ongoing call is not a group call.\n"); + vgcs_vty_notify(trans, "Not a group call\n"); + return -EIO; + } + + if (trans->gcc.fi->state != VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE && + trans->gcc.fi->state != VGCS_GCC_ST_U2sr_GROUP_CALL_ACTIVE) { + LOG_GCC(trans, LOGL_NOTICE, "Cannot listen, we are not talking.\n"); + return -EINVAL; + } + + /* Send LISTEN-REQ to become listener. */ + return osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_LISTEN_REQ, NULL); +} + +int gsm44068_dump_calls(struct osmocom_ms *ms, void (*print)(void *, const char *, ...), void *priv) +{ + struct gsm_trans *trans; + + llist_for_each_entry(trans, &ms->trans_list, entry) { + if (trans->protocol != GSM48_PDISC_GROUP_CC && trans->protocol != GSM48_PDISC_BCAST_CC) + continue; + print(priv, "Voice %s call '%d' is %s.\n", + (trans->protocol == GSM48_PDISC_GROUP_CC) ? "group" : "broadcast", trans->callref, + (trans->gcc.fi->state == VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) ? "incoming" : "active"); + } + + return 0; +} diff --git a/src/host/layer23/src/mobile/gsm480_ss.c b/src/host/layer23/src/mobile/gsm480_ss.c index bf99667..4ed8354 100644 --- a/src/host/layer23/src/mobile/gsm480_ss.c +++ b/src/host/layer23/src/mobile/gsm480_ss.c @@ -28,6 +28,7 @@ #include <osmocom/bb/mobile/mncc.h> #include <osmocom/bb/mobile/transaction.h> #include <osmocom/bb/mobile/gsm480_ss.h> +#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h> #include <osmocom/core/talloc.h> #include <osmocom/bb/mobile/vty.h> #include <osmocom/gsm/protocol/gsm_04_80.h> @@ -599,6 +600,12 @@ return -EIO; } + /* ASCI call does not allow other transactions */ + if (trans_find_ongoing_gcc_bcc(ms)) { + gsm480_ss_result(ms, "<ongoing ASCI call>", 0); + return -EBUSY; + } + /* allocate transaction with dummy reference */ transaction_id = trans_assign_trans_id(ms, GSM48_PDISC_NC_SS, 0); diff --git a/src/host/layer23/src/mobile/gsm48_cc.c b/src/host/layer23/src/mobile/gsm48_cc.c index 190eb91..9691c68 100644 --- a/src/host/layer23/src/mobile/gsm48_cc.c +++ b/src/host/layer23/src/mobile/gsm48_cc.c @@ -32,6 +32,7 @@ #include <osmocom/bb/mobile/mncc.h> #include <osmocom/bb/mobile/transaction.h> #include <osmocom/bb/mobile/gsm48_cc.h> +#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h> #include <osmocom/bb/mobile/voice.h> #include <l1ctl_proto.h> @@ -1934,6 +1935,12 @@ return -EBUSY; } + /* ASCI call does not allow other transactions */ + if (trans_find_ongoing_gcc_bcc(ms)) { + LOGP(DCC, LOGL_NOTICE, "Phone is busy doing ASCI call\n"); + return -EBUSY; + } + data->msg_type = msg_type; /* Find callref */ diff --git a/src/host/layer23/src/mobile/gsm48_mm.c b/src/host/layer23/src/mobile/gsm48_mm.c index 82e5fad..092a984 100644 --- a/src/host/layer23/src/mobile/gsm48_mm.c +++ b/src/host/layer23/src/mobile/gsm48_mm.c @@ -38,6 +38,7 @@ #include <osmocom/bb/mobile/gsm48_cc.h> #include <osmocom/bb/mobile/gsm480_ss.h> #include <osmocom/bb/mobile/gsm411_sms.h> +#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h> #include <osmocom/bb/mobile/app_mobile.h> #include <osmocom/bb/mobile/primitives.h> #include <osmocom/bb/mobile/vty.h> @@ -905,6 +906,7 @@ break; case GSM48_MMGCC_CLASS: case GSM48_MMBCC_CLASS: + gsm44068_rcv_gcc_bcc(ms, msg); break; } msgb_free(msg); @@ -1169,6 +1171,15 @@ /* must exit, because this function can be called recursively */ return; } + + /* Tell the group call that we are ready for a new channel activation. */ + if (state == GSM48_MM_ST_MM_IDLE + && (substate == GSM48_MM_SST_NORMAL_SERVICE + || substate == GSM48_MM_SST_ATTEMPT_UPDATE)) { + gsm44068_rcv_mm_idle(ms); + /* must exit, because this function can be called recursively */ + return; + } } /* return PLMN SEARCH or PLMN SEARCH NORMAL state */ @@ -4751,7 +4762,7 @@ case GSM48_PDISC_GROUP_CC: case GSM48_PDISC_BCAST_CC: - rc = -ENOTSUP; + rc = gsm44068_rcv_gcc_bcc(ms, msg); msgb_free(msg); return rc; diff --git a/src/host/layer23/src/mobile/transaction.c b/src/host/layer23/src/mobile/transaction.c index 712a8ee..3cc25fd 100644 --- a/src/host/layer23/src/mobile/transaction.c +++ b/src/host/layer23/src/mobile/transaction.c @@ -30,6 +30,7 @@ void _gsm48_cc_trans_free(struct gsm_trans *trans); void _gsm480_ss_trans_free(struct gsm_trans *trans); void _gsm411_sms_trans_free(struct gsm_trans *trans); +void _gsm44068_gcc_bcc_trans_free(struct gsm_trans *trans); struct gsm_trans *trans_find_by_id(struct osmocom_ms *ms, uint8_t proto, uint8_t trans_id) @@ -93,6 +94,10 @@ case GSM48_PDISC_SMS: _gsm411_sms_trans_free(trans); break; + case GSM48_PDISC_GROUP_CC: + case GSM48_PDISC_BCAST_CC: + _gsm44068_gcc_bcc_trans_free(trans); + break; } DEBUGP(DCC, "ms %s frees transaction (mem %p)\n", trans->ms->name, -- To view, visit https://gerrit.osmocom.org/c/osmocom-bb/+/34494?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmocom-bb Gerrit-Branch: master Gerrit-Change-Id: I22eacd018f18c18ba83dbd3dd874cf029344674d Gerrit-Change-Number: 34494 Gerrit-PatchSet: 16 Gerrit-Owner: jolly <andreas(a)eversberg.eu> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-MessageType: merged
1 0
0 0
[S] Change in osmocom-bb[master]: Add header length check to received CCCH messages
by laforge 03 Oct '23

03 Oct '23
laforge has submitted this change. ( https://gerrit.osmocom.org/c/osmocom-bb/+/34555?usp=email ) Change subject: Add header length check to received CCCH messages ...................................................................... Add header length check to received CCCH messages Change-Id: I3bc6b0b997e6ea1048c3357d276adfcc1638b02b --- M src/host/layer23/src/mobile/gsm48_rr.c 1 file changed, 14 insertions(+), 0 deletions(-) Approvals: Jenkins Builder: Verified pespin: Looks good to me, but someone else must approve fixeria: Looks good to me, approved diff --git a/src/host/layer23/src/mobile/gsm48_rr.c b/src/host/layer23/src/mobile/gsm48_rr.c index e5e2162..d725642 100644 --- a/src/host/layer23/src/mobile/gsm48_rr.c +++ b/src/host/layer23/src/mobile/gsm48_rr.c @@ -4856,6 +4856,11 @@ { 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); -- To view, visit https://gerrit.osmocom.org/c/osmocom-bb/+/34555?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmocom-bb Gerrit-Branch: master Gerrit-Change-Id: I3bc6b0b997e6ea1048c3357d276adfcc1638b02b Gerrit-Change-Number: 34555 Gerrit-PatchSet: 2 Gerrit-Owner: jolly <andreas(a)eversberg.eu> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-MessageType: merged
1 0
0 0
  • ← Newer
  • 1
  • ...
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • ...
  • 185
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.