summaryrefslogtreecommitdiffstats
path: root/src/host/layer23/src/mobile/gsm48_mm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/host/layer23/src/mobile/gsm48_mm.c')
-rw-r--r--src/host/layer23/src/mobile/gsm48_mm.c1451
1 files changed, 1100 insertions, 351 deletions
diff --git a/src/host/layer23/src/mobile/gsm48_mm.c b/src/host/layer23/src/mobile/gsm48_mm.c
index 872dce55..fad475e2 100644
--- a/src/host/layer23/src/mobile/gsm48_mm.c
+++ b/src/host/layer23/src/mobile/gsm48_mm.c
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
@@ -30,18 +26,24 @@
#include <osmocom/core/utils.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/core/talloc.h>
+#include <osmocom/core/timer.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/networks.h>
#include <osmocom/bb/common/l1ctl.h>
+#include <osmocom/bb/common/utils.h>
+#include <osmocom/bb/common/subscriber.h>
#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>
-#include <osmocom/bb/common/utils.h>
+#include <osmocom/bb/mobile/gsm48_rr.h>
+#include <osmocom/bb/mobile/gsm322.h>
extern void *l23_ctx;
@@ -59,6 +61,9 @@ static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate);
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_reject(struct osmocom_ms *ms, struct msgb *msg);
+static int gsm48_mm_group_rel_req(struct osmocom_ms *ms, struct msgb *msg);
+static int gsm48_mm_uplink_reject(struct osmocom_ms *ms, struct msgb *msg);
/*
* notes
@@ -109,7 +114,7 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg);
* - cell selected
* - cell == SIM LAI
*
- * Otherwhise PLMN SEARCH is entered.
+ * Otherwise PLMN SEARCH is entered.
*
* During PLMN SEARCH NORMAL state: (4.2.2.5)
* - on expirery of T3212: Perform periodic location update, when back
@@ -143,6 +148,9 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg);
* 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:
@@ -175,6 +183,9 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg);
* - 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:
@@ -184,16 +195,56 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg);
* - 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
- * state. Depending on the conditions above, the appropiate state is selected.
+ * state. Depending on the conditions above, the appropriate state is selected.
*
*
* gsm48_mm_return_idle() is used to select the Service state when returning
@@ -222,6 +273,33 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg);
*
* 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.
*/
/*
@@ -271,46 +349,68 @@ static int decode_network_name(char *name, int name_len,
return length;
}
-/* encode 'mobile identity' */
-int gsm48_encode_mi(uint8_t *buf, struct msgb *msg, struct osmocom_ms *ms,
- uint8_t mi_type)
+/* Encode and append 'mobile identity' of given type to message, based on current settings. */
+int gsm48_encode_mi_lv(struct osmocom_ms *ms, struct msgb *msg, uint8_t mi_type, bool emergency_imsi)
{
struct gsm_subscriber *subscr = &ms->subscr;
struct gsm_settings *set = &ms->settings;
- uint8_t *ie;
+ struct osmo_mobile_identity mi = { };
+ int rc;
+ uint8_t *l;
- switch(mi_type) {
+ /* Copy MI values according to their types. */
+ switch (mi_type) {
case GSM_MI_TYPE_TMSI:
- gsm48_generate_mid_from_tmsi(buf, subscr->tmsi);
+ mi.tmsi = subscr->tmsi;
break;
case GSM_MI_TYPE_IMSI:
- gsm48_generate_mid_from_imsi(buf, subscr->imsi);
+ if (emergency_imsi)
+ OSMO_STRLCPY_ARRAY(mi.imsi, set->emergency_imsi);
+ else
+ OSMO_STRLCPY_ARRAY(mi.imsi, subscr->imsi);
break;
case GSM_MI_TYPE_IMEI:
- gsm48_generate_mid_from_imsi(buf, set->imei);
+ OSMO_STRLCPY_ARRAY(mi.imei, set->imei);
break;
case GSM_MI_TYPE_IMEISV:
- gsm48_generate_mid_from_imsi(buf, set->imeisv);
- break;
- case GSM_MI_TYPE_NONE:
- default:
- buf[0] = GSM48_IE_MOBILE_ID;
- buf[1] = 1;
- buf[2] = 0xf0;
+ OSMO_STRLCPY_ARRAY(mi.imeisv, set->imeisv);
break;
}
- /* alter MI type */
- buf[2] = (buf[2] & ~GSM_MI_TYPE_MASK) | mi_type;
- if (msg) {
- /* MI as LV */
- ie = msgb_put(msg, 1 + buf[1]);
- memcpy(ie, buf + 1, 1 + buf[1]);
+ /* Generate MI or 'NONE', if not available. */
+ switch (mi_type) {
+ case GSM_MI_TYPE_TMSI:
+ case GSM_MI_TYPE_IMSI:
+ case GSM_MI_TYPE_IMEI:
+ case GSM_MI_TYPE_IMEISV:
+ mi.type = mi_type;
+ l = msgb_put(msg, 1);
+ rc = osmo_mobile_identity_encode_msgb(msg, &mi, true);
+ if (rc < 0) {
+ LOGP(DMM, LOGL_ERROR, "Failed to encode mobile identity type %d. Please fix!\n", mi_type);
+ *l = 1;
+ msgb_put_u8(msg, 0xf0 | GSM_MI_TYPE_NONE);
+ break;
+ }
+ *l = rc;
+ break;
+ case GSM_MI_TYPE_NONE:
+ default:
+ msgb_put_u8(msg, 1);
+ msgb_put_u8(msg, 0xf0 | mi_type);
}
return 0;
}
+int gsm48_encode_mi_tlv(struct osmocom_ms *ms, struct msgb *msg, uint8_t mi_type, bool emergency_imsi)
+{
+ /* Append IE type. */
+ msgb_put_u8(msg, GSM48_IE_MOBILE_ID);
+
+ return gsm48_encode_mi_lv(ms, msg, mi_type, emergency_imsi);
+}
+
/* encode 'classmark 1' */
int gsm48_encode_classmark1(struct gsm48_classmark1 *cm, uint8_t rev_lev,
uint8_t es_ind, uint8_t a5_1, uint8_t pwr_lev)
@@ -554,6 +654,9 @@ static const struct value_string gsm48_mmevent_names[] = {
{ GSM48_MM_EVENT_SYSINFO, "MM_EVENT_SYSINFO" },
{ 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 }
};
@@ -638,6 +741,46 @@ static const struct value_string gsm48_mmxx_msg_names[] = {
{ 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" },
+ { GSM48_MMBCC_REL_IND, "MMBCC_REL_IND" },
+ { GSM48_MMBCC_DATA_REQ, "MMBCC_DATA_REQ" },
+ { GSM48_MMBCC_DATA_IND, "MMBCC_DATA_IND" },
+ { GSM48_MMBCC_UNIT_DATA_REQ, "MMBCC_UNIT_DATA_REQ" },
+ { GSM48_MMBCC_UNIT_DATA_IND, "MMBCC_UNIT_DATA_IND" },
+ { GSM48_MMBCC_REEST_REQ, "MMBCC_REEST_REQ" },
+ { GSM48_MMBCC_REEST_CNF, "MMBCC_REEST_CNF" },
+ { GSM48_MMBCC_ERR_IND, "MMBCC_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 }
};
@@ -763,6 +906,10 @@ int gsm48_mmxx_dequeue(struct osmocom_ms *ms)
case GSM48_MMSMS_CLASS:
gsm411_rcv_sms(ms, msg);
break;
+ case GSM48_MMGCC_CLASS:
+ case GSM48_MMBCC_CLASS:
+ gsm44068_rcv_gcc_bcc(ms, msg);
+ break;
}
msgb_free(msg);
work = 1; /* work done */
@@ -864,8 +1011,8 @@ const char *gsm48_mm_state_names[] = {
"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"
@@ -881,8 +1028,8 @@ const char *gsm48_mm_substate_names[] = {
"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 */
@@ -925,34 +1072,39 @@ static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate)
&& (mm->state != GSM48_MM_ST_MM_IDLE || mm->substate != substate)) {
switch (substate) {
case GSM48_MM_SST_NORMAL_SERVICE:
- vty_notify(ms, NULL);
- vty_notify(ms, "On Network, normal service: %s, %s\n",
- gsm_get_mcc(plmn->mcc),
- gsm_get_mnc(plmn->mcc, plmn->mnc));
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "On Network, normal service: %s, %s\n",
+ gsm_get_mcc(plmn->plmn.mcc),
+ gsm_get_mnc(&plmn->plmn));
break;
case GSM48_MM_SST_LIMITED_SERVICE:
- vty_notify(ms, NULL);
- vty_notify(ms, "Limited service, emergency calls are "
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Limited service, emergency calls are "
"possible.\n");
break;
case GSM48_MM_SST_PLMN_SEARCH_NORMAL:
case GSM48_MM_SST_PLMN_SEARCH:
- vty_notify(ms, NULL);
- vty_notify(ms, "Searching network...\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Searching network...\n");
break;
case GSM48_MM_SST_NO_IMSI:
- vty_notify(ms, NULL);
- vty_notify(ms, "No SIM, emergency calls are "
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "No SIM, emergency calls are "
"possible.\n");
break;
case GSM48_MM_SST_NO_CELL_AVAIL:
- vty_notify(ms, NULL);
- vty_notify(ms, "No service.\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "No service.\n");
break;
case GSM48_MM_SST_ATTEMPT_UPDATE:
- vty_notify(ms, NULL);
- vty_notify(ms, "Trying to registering with "
- "network...\n");
+ l23_vty_ms_notify(ms, NULL);
+ 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;
}
}
@@ -975,6 +1127,8 @@ static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate)
if (!nmsg)
return;
gsm48_mmevent_msg(ms, nmsg);
+ /* Stop here and wait for the IMSI_DETACH event being handled. */
+ return;
}
/* 4.4.2 start T3212 in MM IDLE mode if not started or has expired */
@@ -1006,7 +1160,7 @@ static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate)
if (s->t3212) /* still required? */
gsm48_mm_loc_upd_periodic(ms, NULL);
else
- LOGP(DMM, LOGL_INFO, "but not requred\n");
+ LOGP(DMM, LOGL_INFO, "but not required\n");
/* must exit, because this function can be called
* recursively
*/
@@ -1021,6 +1175,15 @@ static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate)
/* 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 */
@@ -1042,7 +1205,7 @@ static int gsm48_mm_set_plmn_search(struct osmocom_ms *ms)
"SIM not updated.\n");
return GSM48_MM_SST_PLMN_SEARCH;
}
- if (subscr->lac == 0x0000 || subscr->lac >= 0xfffe) {
+ if (subscr->lai.lac == 0x0000 || subscr->lai.lac >= 0xfffe) {
LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because "
"LAI in SIM not valid.\n");
return GSM48_MM_SST_PLMN_SEARCH;
@@ -1056,15 +1219,12 @@ static int gsm48_mm_set_plmn_search(struct osmocom_ms *ms)
}
/* selected cell's LAI not equal to LAI stored on the sim */
- if (cs->sel_mcc != subscr->mcc
- || cs->sel_mnc != subscr->mnc
- || cs->sel_lac != subscr->lac) {
+ if (osmo_lai_cmp(&cs->sel_cgi.lai, &subscr->lai) != 0) {
LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because "
- "LAI of selected cell (MCC %s MNC %s LAC 0x%04x) "
- "!= LAI in SIM (MCC %s MNC %s LAC 0x%04x).\n",
- gsm_print_mcc(cs->sel_mcc), gsm_print_mnc(cs->sel_mnc),
- cs->sel_lac, gsm_print_mcc(subscr->mcc),
- gsm_print_mnc(subscr->mnc), subscr->lac);
+ "LAI of selected cell (MCC-MNC %s LAC 0x%04x) "
+ "!= LAI in SIM (MCC-MNC %s LAC 0x%04x).\n",
+ osmo_plmn_name(&cs->sel_cgi.lai.plmn), cs->sel_cgi.lai.lac,
+ osmo_plmn_name2(&subscr->lai.plmn), subscr->lai.lac);
return GSM48_MM_SST_PLMN_SEARCH;
}
@@ -1081,6 +1241,14 @@ static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg)
struct gsm322_cellsel *cs = &ms->cellsel;
struct gsm48_sysinfo *s = &cs->sel_si;
+ /* When we are in group transmit mode, we return to group receive mode. */
+ if (mm->vgcs.enabled) {
+ LOGP(DMM, LOGL_INFO, "Return to group receive mode.\n");
+ 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);
+ return 0;
+ }
+
if (cs->state != GSM322_C3_CAMPED_NORMALLY
&& cs->state != GSM322_C7_CAMPED_ANY_CELL) {
LOGP(DMM, LOGL_INFO, "Not camping, wait for CS process to "
@@ -1120,10 +1288,8 @@ static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg)
/* if we are attached and selected cell equals the registered LAI */
if (subscr->imsi_attached
- && subscr->lac /* valid */
- && cs->sel_mcc == subscr->mcc
- && cs->sel_mnc == subscr->mnc
- && cs->sel_lac == subscr->lac) {
+ && subscr->lai.lac /* valid */
+ && (osmo_lai_cmp(&cs->sel_cgi.lai, &subscr->lai) == 0)) {
LOGP(DMM, LOGL_INFO, "We are in registered LAI as returning "
"to MM IDLE\n");
/* if SIM not updated (abnormal case as described in 4.4.4.9) */
@@ -1131,8 +1297,7 @@ static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg)
new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
GSM48_MM_SST_ATTEMPT_UPDATE);
else
- new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
- GSM48_MM_SST_NORMAL_SERVICE);
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NORMAL_SERVICE);
return 0;
}
@@ -1140,27 +1305,22 @@ static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg)
if (cs->state == GSM322_C3_CAMPED_NORMALLY) {
LOGP(DMM, LOGL_INFO, "We are camping normally as returning to "
"MM IDLE\n");
- if (gsm_subscr_is_forbidden_plmn(subscr, cs->sel_mcc,
- cs->sel_mnc)) {
+ if (gsm_subscr_is_forbidden_plmn(subscr, &cs->sel_cgi.lai.plmn)) {
/* 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);
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LIMITED_SERVICE);
} else
- if (gsm322_is_forbidden_la(ms, cs->sel_mcc, cs->sel_mnc,
- cs->sel_lac)) {
+ 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);
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LIMITED_SERVICE);
} else
/* 4.4.4.9 if cell is barred, don't start */
if ((!subscr->acc_barr && s->cell_barr)
|| (!subscr->acc_barr && !((subscr->acc_class & 0xfbff) &
(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);
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LIMITED_SERVICE);
} else {
/* location update allowed */
LOGP(DMM, LOGL_INFO, "Loc. upd. allowed.\n");
@@ -1171,8 +1331,7 @@ static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg)
/* 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);
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LIMITED_SERVICE);
}
return 0;
@@ -1200,6 +1359,13 @@ static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_sysinfo *s = &cs->sel_si;
struct gsm_settings *set = &ms->settings;
+ if (mm->vgcs.enabled) {
+ LOGP(DMM, LOGL_ERROR, "Cell selection in group receive mode, this should not happen.\n");
+ 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);
+ return 0;
+ }
+
/* no SIM is inserted */
if (!subscr->sim_valid) {
LOGP(DMM, LOGL_INFO, "SIM invalid as cell is selected.\n");
@@ -1210,17 +1376,14 @@ static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg)
/* SIM not updated in this LA */
if (subscr->ustate == GSM_SIM_U1_UPDATED
- && subscr->lac /* valid */
- && cs->sel_mcc == subscr->mcc
- && cs->sel_mnc == subscr->mnc
- && cs->sel_lac == subscr->lac
+ && subscr->lai.lac /* valid */
+ && (osmo_lai_cmp(&cs->sel_cgi.lai, &subscr->lai) == 0)
&& !mm->lupd_periodic) {
if (subscr->imsi_attached) {
struct msgb *nmsg;
LOGP(DMM, LOGL_INFO, "Valid in location area.\n");
- new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
- GSM48_MM_SST_NORMAL_SERVICE);
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NORMAL_SERVICE);
/* send message to PLMN search process */
nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS);
@@ -1234,8 +1397,7 @@ static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg)
struct msgb *nmsg;
LOGP(DMM, LOGL_INFO, "Attachment not required.\n");
- new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
- GSM48_MM_SST_NORMAL_SERVICE);
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NORMAL_SERVICE);
/* send message to PLMN search process */
nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS);
@@ -1251,14 +1413,12 @@ static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg)
/* PLMN mode auto and selected cell is forbidden */
if (set->plmn_mode == PLMN_MODE_AUTO
&& (!cs->selected
- || gsm_subscr_is_forbidden_plmn(subscr, cs->sel_mcc, cs->sel_mnc)
- || gsm322_is_forbidden_la(ms, cs->sel_mcc, cs->sel_mnc,
- cs->sel_lac))) {
+ || gsm_subscr_is_forbidden_plmn(subscr, &cs->sel_cgi.lai.plmn)
+ || gsm322_is_forbidden_la(ms, &cs->sel_cgi.lai))) {
struct msgb *nmsg;
LOGP(DMM, LOGL_INFO, "Selected cell is forbidden.\n");
- new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
- GSM48_MM_SST_LIMITED_SERVICE);
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LIMITED_SERVICE);
/* send message to PLMN search process */
nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_FAILED);
@@ -1273,14 +1433,12 @@ static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg)
* in M3 state the PLMN is not selected for registration. */
if (set->plmn_mode == PLMN_MODE_MANUAL
&& (!cs->selected
- || plmn->mcc != cs->sel_mcc
- || plmn->mnc != cs->sel_mnc
+ || (osmo_plmn_cmp(&plmn->plmn, &cs->sel_cgi.lai.plmn) != 0)
|| plmn->state == GSM322_M3_NOT_ON_PLMN)) {
struct msgb *nmsg;
LOGP(DMM, LOGL_INFO, "Selected cell not found.\n");
- new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
- GSM48_MM_SST_LIMITED_SERVICE);
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LIMITED_SERVICE);
/* send message to PLMN search process */
nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_FAILED);
@@ -1388,9 +1546,8 @@ uint32_t mm_conn_new_ref = 0x80000001;
/* new MM connection state */
static void new_conn_state(struct gsm48_mm_conn *conn, int state)
{
- LOGP(DMM, LOGL_INFO, "(ref %x) new state %s -> %s\n", conn->ref,
- gsm48_mmxx_state_names[conn->state],
- gsm48_mmxx_state_names[state]);
+ LOGP(DMM, LOGL_INFO, "(ref 0x%x proto %d) new state %s -> %s\n", conn->ref, conn->protocol,
+ gsm48_mmxx_state_names[conn->state], gsm48_mmxx_state_names[state]);
conn->state = state;
}
@@ -1409,13 +1566,35 @@ struct gsm48_mm_conn *mm_conn_by_id(struct gsm48_mmlayer *mm,
}
/* find MM connection by reference */
-struct gsm48_mm_conn *mm_conn_by_ref(struct gsm48_mmlayer *mm,
- uint32_t ref)
+struct gsm48_mm_conn *mm_conn_by_ref_and_class(struct gsm48_mmlayer *mm,
+ uint32_t ref, uint16_t cls)
{
struct gsm48_mm_conn *conn;
+ uint8_t protocol;
+
+ switch (cls) {
+ case GSM48_MMCC_CLASS:
+ protocol = GSM48_PDISC_CC;
+ break;
+ case GSM48_MMSS_CLASS:
+ protocol = GSM48_PDISC_NC_SS;
+ break;
+ case GSM48_MMSMS_CLASS:
+ protocol = GSM48_PDISC_SMS;
+ break;
+ case GSM48_MMGCC_CLASS:
+ protocol = GSM48_PDISC_GROUP_CC;
+ break;
+ case GSM48_MMBCC_CLASS:
+ protocol = GSM48_PDISC_BCAST_CC;
+ break;
+ default:
+ LOGP(DMM, LOGL_ERROR, "Invalid message class 0x%03x. Please fix!", cls);
+ return NULL;
+ }
llist_for_each_entry(conn, &mm->mm_conn, list) {
- if (conn->ref == ref)
+ if (conn->ref == ref && conn->protocol == protocol)
return conn;
}
return NULL;
@@ -1465,6 +1644,7 @@ static int gsm48_mm_release_mm_conn(struct osmocom_ms *ms, int abort_any,
struct gsm48_mm_conn *conn, *conn2;
struct msgb *nmsg;
struct gsm48_mmxx_hdr *nmmh;
+ int msg_type;
/* Note: For SAPI 0 all connections are released */
@@ -1480,35 +1660,44 @@ static int gsm48_mm_release_mm_conn(struct osmocom_ms *ms, int abort_any,
/* abort any OR the pending connection */
if ((abort_any || conn->state == GSM48_MMXX_ST_CONN_PEND)
&& (sapi == conn->sapi || sapi == 0)) {
- /* send MMxx-REL-IND */
- nmsg = NULL;
+ /* send MMXX-REL-IND or MMXX-ERR-IND */
switch(conn->protocol) {
case GSM48_PDISC_CC:
- nmsg = gsm48_mmxx_msgb_alloc(
- error ? GSM48_MMCC_ERR_IND
- : GSM48_MMCC_REL_IND, conn->ref,
- conn->transaction_id,
- conn->sapi);
+ msg_type = (error) ? GSM48_MMCC_ERR_IND
+ : GSM48_MMCC_REL_IND;
break;
case GSM48_PDISC_NC_SS:
- nmsg = gsm48_mmxx_msgb_alloc(
- error ? GSM48_MMSS_ERR_IND
- : GSM48_MMSS_REL_IND, conn->ref,
- conn->transaction_id,
- conn->sapi);
+ msg_type = (error) ? GSM48_MMSS_ERR_IND
+ : GSM48_MMSS_REL_IND;
break;
case GSM48_PDISC_SMS:
- nmsg = gsm48_mmxx_msgb_alloc(
- error ? GSM48_MMSMS_ERR_IND
- : GSM48_MMSMS_REL_IND, conn->ref,
- conn->transaction_id,
- conn->sapi);
+ msg_type = (error) ? GSM48_MMSMS_ERR_IND
+ : GSM48_MMSMS_REL_IND;
+ break;
+ case GSM48_PDISC_GROUP_CC:
+ msg_type = (error) ? GSM48_MMGCC_ERR_IND
+ : GSM48_MMGCC_REL_IND;
break;
+ case GSM48_PDISC_BCAST_CC:
+ msg_type = (error) ? GSM48_MMBCC_ERR_IND
+ : GSM48_MMBCC_REL_IND;
+ break;
+ default:
+ msg_type = -1;
}
- if (!nmsg) {
+ if (msg_type == -1) {
/* this should not happen */
+ LOGP(DMM, LOGL_ERROR, "MM connection of "
+ "unsupported protocol? Please fix!\n");
+ mm_conn_free(conn);
+ continue;
+ }
+ nmsg = gsm48_mmxx_msgb_alloc(msg_type, conn->ref,
+ conn->transaction_id,
+ conn->sapi);
+ if (!nmsg) {
mm_conn_free(conn);
- continue; /* skip if not of CC type */
+ continue;
}
nmmh = (struct gsm48_mmxx_hdr *)nmsg->data;
nmmh->cause = cause;
@@ -1584,7 +1773,7 @@ static int gsm48_mm_rx_tmsi_realloc_cmd(struct osmocom_ms *ms, struct msgb *msg)
return -EINVAL;
}
/* LAI */
- gsm48_decode_lai_hex(lai, &subscr->mcc, &subscr->mnc, &subscr->lac);
+ gsm48_decode_lai2(lai, &subscr->lai);
/* MI */
mi = gh->data + sizeof(struct gsm48_loc_area_id);
mi_type = mi[1] & GSM_MI_TYPE_MASK;
@@ -1600,12 +1789,12 @@ static int gsm48_mm_rx_tmsi_realloc_cmd(struct osmocom_ms *ms, struct msgb *msg)
gsm48_mm_tx_tmsi_reall_cpl(ms);
break;
case GSM_MI_TYPE_IMSI:
- subscr->tmsi = 0xffffffff;
+ subscr->tmsi = GSM_RESERVED_TMSI;
LOGP(DMM, LOGL_INFO, "TMSI removed.\n");
gsm48_mm_tx_tmsi_reall_cpl(ms);
break;
default:
- subscr->tmsi = 0xffffffff;
+ subscr->tmsi = GSM_RESERVED_TMSI;
LOGP(DMM, LOGL_NOTICE, "TMSI reallocation with unknown MI "
"type %d.\n", mi_type);
gsm48_mm_tx_mm_status(ms, GSM48_REJECT_INCORRECT_MESSAGE);
@@ -1697,8 +1886,8 @@ static int gsm48_mm_rx_auth_rej(struct osmocom_ms *ms, struct msgb *msg)
subscr->sim_valid = 0;
/* TMSI and LAI invalid */
- subscr->tmsi = 0xffffffff;
- subscr->lac = 0x0000;
+ subscr->tmsi = GSM_RESERVED_TMSI;
+ subscr->lai.lac = 0x0000;
/* key is invalid */
subscr->key_seq = 7;
@@ -1754,7 +1943,7 @@ static int gsm48_mm_rx_id_req(struct osmocom_ms *ms, struct msgb *msg)
return gsm48_mm_tx_mm_status(ms,
GSM48_REJECT_MSG_NOT_COMPATIBLE);
}
- if (mi_type == GSM_MI_TYPE_TMSI && subscr->tmsi == 0xffffffff) {
+ if (mi_type == GSM_MI_TYPE_TMSI && subscr->tmsi == GSM_RESERVED_TMSI) {
LOGP(DMM, LOGL_INFO, "IDENTITY REQUEST of TMSI, but we have no "
"TMSI\n");
return gsm48_mm_tx_mm_status(ms,
@@ -1769,7 +1958,6 @@ static int gsm48_mm_tx_id_rsp(struct osmocom_ms *ms, uint8_t mi_type)
{
struct msgb *nmsg;
struct gsm48_hdr *ngh;
- uint8_t buf[11];
LOGP(DMM, LOGL_INFO, "IDENTITY RESPONSE\n");
@@ -1781,8 +1969,8 @@ static int gsm48_mm_tx_id_rsp(struct osmocom_ms *ms, uint8_t mi_type)
ngh->proto_discr = GSM48_PDISC_MM;
ngh->msg_type = GSM48_MT_MM_ID_RESP;
- /* MI */
- gsm48_encode_mi(buf, nmsg, ms, mi_type);
+ /* MI (LV) */
+ gsm48_encode_mi_lv(ms, nmsg, mi_type, false);
/* push RR header and send down */
return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_DATA_REQ, 0, 0);
@@ -1800,7 +1988,6 @@ static int gsm48_mm_tx_imsi_detach(struct osmocom_ms *ms, int rr_prim)
struct msgb *nmsg;
struct gsm48_hdr *ngh;
uint8_t pwr_lev;
- uint8_t buf[11];
struct gsm48_classmark1 cm;
@@ -1821,13 +2008,13 @@ static int gsm48_mm_tx_imsi_detach(struct osmocom_ms *ms, int rr_prim)
pwr_lev = gsm48_current_pwr_lev(set, rr->cd_now.arfcn);
gsm48_encode_classmark1(&cm, sup->rev_lev, sup->es_ind, set->a5_1,
pwr_lev);
- msgb_v_put(nmsg, *((uint8_t *)&cm));
- /* MI */
- if (subscr->tmsi != 0xffffffff) { /* have TMSI ? */
- gsm48_encode_mi(buf, nmsg, ms, GSM_MI_TYPE_TMSI);
+ msgb_v_put(nmsg, *((uint8_t *)&cm));
+ /* MI (LV) */
+ if (subscr->tmsi != GSM_RESERVED_TMSI) { /* have TMSI ? */
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_TMSI, false);
LOGP(DMM, LOGL_INFO, " using TMSI 0x%08x\n", subscr->tmsi);
} else {
- gsm48_encode_mi(buf, nmsg, ms, GSM_MI_TYPE_IMSI);
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, false);
LOGP(DMM, LOGL_INFO, " using IMSI %s\n", subscr->imsi);
}
@@ -1962,6 +2149,34 @@ static int gsm48_mm_imsi_detach_release(struct osmocom_ms *ms, struct msgb *msg)
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;
+
+ LOGP(DMM, LOGL_INFO, "IMSI detach delayed until group receive/transmit mode is left.\n");
+
+ /* 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)
{
@@ -2016,8 +2231,8 @@ static int gsm48_mm_rx_abort(struct osmocom_ms *ms, struct msgb *msg)
subscr->sim_valid = 0;
/* TMSI and LAI invalid */
- subscr->tmsi = 0xffffffff;
- subscr->lac = 0x0000;
+ subscr->tmsi = GSM_RESERVED_TMSI;
+ subscr->lai.lac = 0x0000;
/* key is invalid */
subscr->key_seq = 7;
@@ -2044,11 +2259,13 @@ static int gsm48_mm_rx_info(struct osmocom_ms *ms, struct msgb *msg)
struct tlv_parsed tp;
if (payload_len < 0) {
- LOGP(DMM, LOGL_NOTICE, "Short read of MM INFORMATION message "
- "error.\n");
+ LOGP(DMM, LOGL_ERROR, "Short read of MM INFORMATION message\n");
+ return -EINVAL;
+ }
+ if (tlv_parse(&tp, &gsm48_mm_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DMM, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
return -EINVAL;
}
- tlv_parse(&tp, &gsm48_mm_att_tlvdef, gh->data, payload_len, 0, 0);
/* long name */
if (TLVP_PRESENT(&tp, GSM48_IE_NAME_LONG)) {
@@ -2078,6 +2295,8 @@ static int gsm48_mm_sysinfo(struct osmocom_ms *ms, struct msgb *msg)
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;
@@ -2135,7 +2354,7 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg)
/* (re)start only if we still require location update */
if (!mm->lupd_pending) {
LOGP(DMM, LOGL_INFO, "No loc. upd. pending.\n");
- /* use MM IDLE to selecte the idle state */
+ /* use MM IDLE to select the idle state */
return gsm48_mm_return_idle(ms, NULL);
}
@@ -2159,7 +2378,7 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg)
if (!nmsg)
return -ENOMEM;
gsm322_plmn_sendmsg(ms, nmsg);
- /* use MM IDLE to selecte the idle state */
+ /* use MM IDLE to select the idle state */
return gsm48_mm_return_idle(ms, NULL);
}
@@ -2169,20 +2388,18 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg)
"forbidden PLMN.\n");
LOGP(DSUM, LOGL_INFO, "Location updating is disabled by "
"configuration\n");
- gsm_subscr_add_forbidden_plmn(subscr, cs->sel_mcc,
- cs->sel_mnc, GSM48_REJECT_PLMN_NOT_ALLOWED);
+ gsm_subscr_add_forbidden_plmn(subscr, &cs->sel_cgi.lai.plmn, GSM48_REJECT_PLMN_NOT_ALLOWED);
msg_type = GSM322_EVENT_REG_FAILED;
goto _stop;
}
/* if LAI is forbidden, don't start */
- if (gsm_subscr_is_forbidden_plmn(subscr, cs->sel_mcc, cs->sel_mnc)) {
+ if (gsm_subscr_is_forbidden_plmn(subscr, &cs->sel_cgi.lai.plmn)) {
LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed PLMN.\n");
msg_type = GSM322_EVENT_REG_FAILED;
goto stop;
}
- if (gsm322_is_forbidden_la(ms, cs->sel_mcc,
- cs->sel_mnc, cs->sel_lac)) {
+ if (gsm322_is_forbidden_la(ms, &cs->sel_cgi.lai)) {
LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed LA.\n");
msg_type = GSM322_EVENT_REG_FAILED;
goto stop;
@@ -2197,13 +2414,9 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg)
goto stop;
}
- mm->lupd_mcc = cs->sel_mcc;
- mm->lupd_mnc = cs->sel_mnc;
- mm->lupd_lac = cs->sel_lac;
+ memcpy(&mm->lupd_lai, &cs->sel_cgi.lai, sizeof(mm->lupd_lai));
- LOGP(DSUM, LOGL_INFO, "Perform location update (MCC %s, MNC %s "
- "LAC 0x%04x)\n", gsm_print_mcc(mm->lupd_mcc),
- gsm_print_mnc(mm->lupd_mnc), mm->lupd_lac);
+ LOGP(DSUM, LOGL_INFO, "Perform location update (LAI=%s)\n", osmo_lai_name(&mm->lupd_lai));
return gsm48_mm_tx_loc_upd_req(ms);
}
@@ -2215,6 +2428,7 @@ static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg)
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 */
@@ -2246,17 +2460,16 @@ static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg)
/* if location update is not required */
if (subscr->ustate == GSM_SIM_U1_UPDATED
&& cs->selected
- && cs->sel_mcc == subscr->mcc
- && cs->sel_mnc == subscr->mnc
- && cs->sel_lac == subscr->lac
+ && (osmo_lai_cmp(&cs->sel_cgi.lai, &subscr->lai) == 0)
&& (subscr->imsi_attached
|| !s->att_allowed)) {
LOGP(DMM, LOGL_INFO, "Loc. upd. not required.\n");
- subscr->imsi_attached = 1;
+ subscr->imsi_attached = true;
/* 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 */
@@ -2276,9 +2489,7 @@ static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg)
/* 4.4.3 is attachment required? */
if (subscr->ustate == GSM_SIM_U1_UPDATED
&& cs->selected
- && cs->sel_mcc == subscr->mcc
- && cs->sel_mnc == subscr->mnc
- && cs->sel_lac == subscr->lac
+ && (osmo_lai_cmp(&cs->sel_cgi.lai, &subscr->lai) == 0)
&& !subscr->imsi_attached
&& s->att_allowed) {
/* do location update for IMSI attach */
@@ -2335,7 +2546,6 @@ static int gsm48_mm_tx_loc_upd_req(struct osmocom_ms *ms)
struct gsm48_hdr *ngh;
struct gsm48_loc_upd_req *nlu; /* NOTE: mi_len is part of struct */
uint8_t pwr_lev;
- uint8_t buf[11];
LOGP(DMM, LOGL_INFO, "LOCATION UPDATING REQUEST\n");
@@ -2343,7 +2553,8 @@ static int gsm48_mm_tx_loc_upd_req(struct osmocom_ms *ms)
if (!nmsg)
return -ENOMEM;
ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh));
- nlu = (struct gsm48_loc_upd_req *)msgb_put(nmsg, sizeof(*nlu));
+ /* Do not add mi_len to the message, this is done at gsm48_encode_mi_lv(). */
+ nlu = (struct gsm48_loc_upd_req *)msgb_put(nmsg, sizeof(*nlu) - 1);
ngh->proto_discr = GSM48_PDISC_MM;
ngh->msg_type = GSM48_MT_MM_LOC_UPD_REQUEST;
@@ -2356,24 +2567,20 @@ static int gsm48_mm_tx_loc_upd_req(struct osmocom_ms *ms)
*
* NOTE: The TMSI is only valid within a LAI!
*/
- gsm48_encode_lai_hex(&nlu->lai, subscr->mcc, subscr->mnc, subscr->lac);
- LOGP(DMM, LOGL_INFO, " using LAI (mcc %s mnc %s " "lac 0x%04x)\n",
- gsm_print_mcc(subscr->mcc),
- gsm_print_mnc(subscr->mnc), subscr->lac);
+ gsm48_generate_lai2(&nlu->lai, &subscr->lai);
+ LOGP(DMM, LOGL_INFO, " using LAI=%s\n", osmo_lai_name(&subscr->lai));
/* classmark 1 */
pwr_lev = gsm48_current_pwr_lev(set, cs->sel_arfcn);
gsm48_encode_classmark1(&nlu->classmark1, sup->rev_lev, sup->es_ind,
set->a5_1, pwr_lev);
- /* MI */
- if (subscr->tmsi != 0xffffffff) { /* have TMSI ? */
- gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_TMSI);
+ /* MI (LV) */
+ if (subscr->tmsi != GSM_RESERVED_TMSI) { /* have TMSI ? */
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_TMSI, false);
LOGP(DMM, LOGL_INFO, " using TMSI 0x%08x\n", subscr->tmsi);
} else {
- gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_IMSI);
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, false);
LOGP(DMM, LOGL_INFO, " using IMSI %s\n", subscr->imsi);
}
- msgb_put(nmsg, buf[1]); /* length is part of nlu */
- memcpy(&nlu->mi_len, buf + 1, 1 + buf[1]);
new_mm_state(mm, GSM48_MM_ST_WAIT_RR_CONN_LUPD, 0);
@@ -2406,27 +2613,29 @@ static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg)
struct tlv_parsed tp;
struct msgb *nmsg;
- if (payload_len < sizeof(struct gsm48_loc_area_id)) {
- short_read:
- LOGP(DMM, LOGL_NOTICE, "Short read of LOCATION UPDATING ACCEPT "
- "message error.\n");
+ if (payload_len < sizeof(*lai)) {
+short_read:
+ LOGP(DMM, LOGL_ERROR, "Short read of LOCATION UPDATING ACCEPT message\n");
+ return -EINVAL;
+ }
+ if (tlv_parse(&tp, &gsm48_mm_att_tlvdef,
+ gh->data + sizeof(*lai),
+ payload_len - sizeof(*lai), 0, 0) < 0) {
+ LOGP(DMM, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
return -EINVAL;
}
- tlv_parse(&tp, &gsm48_mm_att_tlvdef,
- gh->data + sizeof(struct gsm48_loc_area_id),
- payload_len - sizeof(struct gsm48_loc_area_id), 0, 0);
/* update has finished */
mm->lupd_pending = 0;
- /* RA was successfull */
+ /* RA was successful */
mm->lupd_ra_failure = 0;
/* stop periodic location updating timer */
stop_mm_t3212(mm); /* 4.4.2 */
/* LAI */
- gsm48_decode_lai_hex(lai, &subscr->mcc, &subscr->mnc, &subscr->lac);
+ gsm48_decode_lai2(lai, &subscr->lai);
/* stop location update timer */
stop_mm_t3210(mm);
@@ -2435,7 +2644,7 @@ static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg)
mm->lupd_attempt = 0;
/* mark SIM as attached */
- subscr->imsi_attached = 1;
+ subscr->imsi_attached = true;
/* set the status in the sim to updated */
new_sim_ustate(subscr, GSM_SIM_U1_UPDATED);
@@ -2444,19 +2653,17 @@ static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg)
gsm_subscr_write_loci(ms);
/* set last registered PLMN */
- if (subscr->lac > 0x0000 && subscr->lac < 0xfffe) {
+ if (subscr->lai.lac > 0x0000 && subscr->lai.lac < 0xfffe) {
subscr->plmn_valid = 1;
- subscr->plmn_mcc = subscr->mcc;
- subscr->plmn_mnc = subscr->mnc;
+ memcpy(&subscr->plmn, &subscr->lai.plmn, sizeof(struct osmo_plmn_id));
}
LOGP(DSUM, LOGL_INFO, "Location update accepted\n");
- LOGP(DMM, LOGL_INFO, "LOCATION UPDATING ACCEPT (mcc %s mnc %s "
- "lac 0x%04x)\n", gsm_print_mcc(subscr->mcc),
- gsm_print_mnc(subscr->mnc), subscr->lac);
+ LOGP(DMM, LOGL_INFO, "LOCATION UPDATING ACCEPT (lai=%s)\n",
+ osmo_lai_name(&subscr->lai));
/* remove LA from forbidden list */
- gsm322_del_forbidden_la(ms, subscr->mcc, subscr->mnc, subscr->lac);
+ gsm322_del_forbidden_la(ms, &subscr->lai);
/* MI */
if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID)) {
@@ -2486,7 +2693,7 @@ static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg)
break;
case GSM_MI_TYPE_IMSI:
LOGP(DMM, LOGL_INFO, "TMSI removed\n");
- subscr->tmsi = 0xffffffff;
+ subscr->tmsi = GSM_RESERVED_TMSI;
/* store LOCI on sim */
gsm_subscr_write_loci(ms);
@@ -2507,7 +2714,7 @@ static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg)
gsm322_plmn_sendmsg(ms, nmsg);
/* follow on proceed */
- if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID))
+ if (TLVP_PRESENT(&tp, GSM48_IE_FOLLOW_ON_PROC))
LOGP(DMM, LOGL_NOTICE, "follow-on proceed not supported.\n");
/* start RR release timer */
@@ -2531,7 +2738,7 @@ static int gsm48_mm_rx_loc_upd_rej(struct osmocom_ms *ms, struct msgb *msg)
return -EINVAL;
}
- /* RA was successfull */
+ /* RA was successful */
mm->lupd_ra_failure = 0;
/* stop periodic location updating timer */
@@ -2581,8 +2788,8 @@ static int gsm48_mm_rel_loc_upd_rej(struct osmocom_ms *ms, struct msgb *msg)
case GSM48_REJECT_LOC_NOT_ALLOWED:
case GSM48_REJECT_ROAMING_NOT_ALLOWED:
/* TMSI and LAI invalid */
- subscr->tmsi = 0xffffffff;
- subscr->lac = 0x0000;
+ subscr->tmsi = GSM_RESERVED_TMSI;
+ subscr->lai.lac = 0x0000;
/* key is invalid */
subscr->key_seq = 7;
@@ -2631,15 +2838,13 @@ static int gsm48_mm_rel_loc_upd_rej(struct osmocom_ms *ms, struct msgb *msg)
LOGP(DSUM, LOGL_INFO, "Location update failed (Illegal ME)\n");
break;
case GSM48_REJECT_PLMN_NOT_ALLOWED:
- gsm_subscr_add_forbidden_plmn(subscr, mm->lupd_mcc,
- mm->lupd_mnc, mm->lupd_rej_cause);
+ gsm_subscr_add_forbidden_plmn(subscr, &mm->lupd_lai.plmn, mm->lupd_rej_cause);
LOGP(DSUM, LOGL_INFO, "Location update failed (PLMN not "
"allowed)\n");
break;
case GSM48_REJECT_LOC_NOT_ALLOWED:
case GSM48_REJECT_ROAMING_NOT_ALLOWED:
- gsm322_add_forbidden_la(ms, mm->lupd_mcc, mm->lupd_mnc,
- mm->lupd_lac, mm->lupd_rej_cause);
+ gsm322_add_forbidden_la(ms, &mm->lupd_lai, mm->lupd_rej_cause);
LOGP(DSUM, LOGL_INFO, "Location update failed (LAI not "
"allowed)\n");
break;
@@ -2674,7 +2879,7 @@ static int gsm48_mm_loc_upd_delay_retry(struct osmocom_ms *ms, struct msgb *msg)
return 0;
}
-/* process failues as described in the lower part of 4.4.4.9 */
+/* process failures as described in the lower part of 4.4.4.9 */
static int gsm48_mm_loc_upd_failed(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm48_mmlayer *mm = &ms->mmlayer;
@@ -2686,9 +2891,7 @@ static int gsm48_mm_loc_upd_failed(struct osmocom_ms *ms, struct msgb *msg)
stop_mm_t3210(mm);
if (subscr->ustate == GSM_SIM_U1_UPDATED
- && mm->lupd_mcc == subscr->mcc
- && mm->lupd_mnc == subscr->mnc
- && mm->lupd_lac == subscr->lac) {
+ && (osmo_lai_cmp(&mm->lupd_lai, &subscr->lai) == 0)) {
if (mm->lupd_attempt < 4) {
LOGP(DSUM, LOGL_INFO, "Try location update later\n");
LOGP(DMM, LOGL_INFO, "Loc. upd. failed, retry #%d\n",
@@ -2704,8 +2907,8 @@ static int gsm48_mm_loc_upd_failed(struct osmocom_ms *ms, struct msgb *msg)
}
/* TMSI and LAI invalid */
- subscr->tmsi = 0xffffffff;
- subscr->lac = 0x0000;
+ subscr->tmsi = GSM_RESERVED_TMSI;
+ subscr->lai.lac = 0x0000;
/* key is invalid */
subscr->key_seq = 7;
@@ -2755,7 +2958,7 @@ static int gsm48_mm_rel_loc_upd_abort(struct osmocom_ms *ms, struct msgb *msg)
return 0;
}
- /* RA was successfull or sent twice */
+ /* RA was successful or sent twice */
mm->lupd_ra_failure = 0;
/* continue with failure handling */
@@ -2797,7 +3000,6 @@ static int gsm48_mm_tx_cm_serv_req(struct osmocom_ms *ms, int rr_prim,
struct gsm48_hdr *ngh;
struct gsm48_service_request *nsr; /* NOTE: includes MI length */
uint8_t *cm2lv;
- uint8_t buf[11];
LOGP(DMM, LOGL_INFO, "CM SERVICE REQUEST (cause %d)\n", mm->est_cause);
@@ -2805,7 +3007,8 @@ static int gsm48_mm_tx_cm_serv_req(struct osmocom_ms *ms, int rr_prim,
if (!nmsg)
return -ENOMEM;
ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh));
- nsr = (struct gsm48_service_request *)msgb_put(nmsg, sizeof(*nsr));
+ /* Do not add mi_len to the message, this is done at gsm48_encode_mi_lv(). */
+ nsr = (struct gsm48_service_request *)msgb_put(nmsg, sizeof(*nsr) - 1);
cm2lv = (uint8_t *)&nsr->classmark;
ngh->proto_discr = GSM48_PDISC_MM;
@@ -2823,23 +3026,21 @@ static int gsm48_mm_tx_cm_serv_req(struct osmocom_ms *ms, int rr_prim,
if (mm->est_cause == RR_EST_CAUSE_EMERGENCY && set->emergency_imsi[0]) {
LOGP(DMM, LOGL_INFO, "-> Using IMSI %s for emergency\n",
set->emergency_imsi);
- gsm48_generate_mid_from_imsi(buf, set->emergency_imsi);
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, true);
} else
if (!subscr->sim_valid) { /* have no SIM ? */
LOGP(DMM, LOGL_INFO, "-> Using IMEI %s\n",
set->imei);
- gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_IMEI);
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMEI, false);
} else
- if (subscr->tmsi != 0xffffffff) { /* have TMSI ? */
- gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_TMSI);
+ if (subscr->tmsi != GSM_RESERVED_TMSI) { /* have TMSI ? */
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_TMSI, false);
LOGP(DMM, LOGL_INFO, "-> Using TMSI\n");
} else {
- gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_IMSI);
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, false);
LOGP(DMM, LOGL_INFO, "-> Using IMSI %s\n",
subscr->imsi);
}
- msgb_put(nmsg, buf[1]); /* length is part of nsr */
- memcpy(&nsr->mi_len, buf + 1, 1 + buf[1]);
/* prio is optional for eMLPP */
/* push RR header and send down */
@@ -2912,8 +3113,8 @@ static int gsm48_mm_rx_cm_service_rej(struct osmocom_ms *ms, struct msgb *msg)
abort_any = 1;
/* TMSI and LAI invalid */
- subscr->tmsi = 0xffffffff;
- subscr->lac = 0x0000;
+ subscr->tmsi = GSM_RESERVED_TMSI;
+ subscr->lai.lac = 0x0000;
/* key is invalid */
subscr->key_seq = 7;
@@ -2938,7 +3139,7 @@ static int gsm48_mm_rx_cm_service_rej(struct osmocom_ms *ms, struct msgb *msg)
/* release MM connection(s) */
gsm48_mm_release_mm_conn(ms, abort_any, 16, 0, 0);
- /* state depends on the existance of remaining MM connections */
+ /* state depends on the existence of remaining MM connections */
if (llist_empty(&mm->mm_conn))
new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0);
else
@@ -2989,21 +3190,10 @@ static int gsm48_mm_init_mm(struct osmocom_ms *ms, struct msgb *msg,
*/
sapi = conn_found->sapi;
reject:
- nmsg = NULL;
- switch(msg_type) {
- case GSM48_MMCC_EST_REQ:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_IND,
- mmh->ref, mmh->transaction_id, sapi);
- break;
- case GSM48_MMSS_EST_REQ:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_REL_IND,
- mmh->ref, mmh->transaction_id, sapi);
- break;
- case GSM48_MMSMS_EST_REQ:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_REL_IND,
- mmh->ref, mmh->transaction_id, sapi);
- break;
- }
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMXX_REL_IND |
+ (msg_type & GSM48_MMXX_MASK),
+ mmh->ref, mmh->transaction_id,
+ sapi);
if (!nmsg)
return -ENOMEM;
nmmh = (struct gsm48_mmxx_hdr *)nmsg->data;
@@ -3077,6 +3267,16 @@ static int gsm48_mm_init_mm(struct osmocom_ms *ms, struct msgb *msg,
cm_serv = GSM48_CMSERV_SMS;
proto = GSM48_PDISC_SMS;
break;
+ case GSM48_MMGCC_EST_REQ:
+ cause = RR_EST_CAUSE_OTHER_SDCCH;
+ cm_serv = GSM48_CMSERV_VGCS;
+ proto = GSM48_PDISC_GROUP_CC;
+ break;
+ case GSM48_MMBCC_EST_REQ:
+ cause = RR_EST_CAUSE_OTHER_SDCCH;
+ cm_serv = GSM48_CMSERV_VBS;
+ proto = GSM48_PDISC_BCAST_CC;
+ break;
}
/* create MM connection instance */
@@ -3212,21 +3412,10 @@ static int gsm48_mm_init_mm_reject(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_mmxx_hdr *nmmh;
/* reject */
- nmsg = NULL;
- switch(msg_type) {
- case GSM48_MMCC_EST_REQ:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_IND, mmh->ref,
- mmh->transaction_id, sapi);
- break;
- case GSM48_MMSS_EST_REQ:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_REL_IND, mmh->ref,
- mmh->transaction_id, sapi);
- break;
- case GSM48_MMSMS_EST_REQ:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_REL_IND, mmh->ref,
- mmh->transaction_id, sapi);
- break;
- }
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMXX_REL_IND |
+ (msg_type & GSM48_MMXX_MASK),
+ mmh->ref, mmh->transaction_id,
+ sapi);
if (!nmsg)
return -ENOMEM;
nmmh = (struct gsm48_mmxx_hdr *)nmsg->data;
@@ -3292,6 +3481,16 @@ static int gsm48_mm_conn_go_dedic(struct osmocom_ms *ms)
conn_found->ref, conn_found->transaction_id,
conn_found->sapi);
break;
+ case GSM48_PDISC_GROUP_CC:
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMGCC_EST_CNF,
+ conn_found->ref, conn_found->transaction_id,
+ conn_found->sapi);
+ break;
+ case GSM48_PDISC_BCAST_CC:
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMBCC_EST_CNF,
+ conn_found->ref, conn_found->transaction_id,
+ conn_found->sapi);
+ break;
}
if (!nmsg)
return -ENOMEM;
@@ -3358,6 +3557,7 @@ static int gsm48_mm_abort_mm_con(struct osmocom_ms *ms, struct msgb *msg)
{
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 */
@@ -3377,11 +3577,13 @@ static int gsm48_mm_abort_mm_con(struct osmocom_ms *ms, struct msgb *msg)
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);
@@ -3395,7 +3597,7 @@ static int gsm48_mm_timeout_mm_con(struct osmocom_ms *ms, struct msgb *msg)
/* release pending connection */
gsm48_mm_release_mm_conn(ms, 0, 102, 0, 0);
- /* state depends on the existance of remaining MM connections */
+ /* state depends on the existence of remaining MM connections */
if (llist_empty(&mm->mm_conn)) {
/* start RR release timer */
start_mm_t3240(mm);
@@ -3425,37 +3627,32 @@ static int gsm48_mm_data(struct osmocom_ms *ms, struct msgb *msg)
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(mm, mmh->ref);
- if (!conn) {
- LOGP(DMM, LOGL_INFO, "MMXX_DATA_REQ with unknown (already "
- "released) ref=%x, sending MMXX_REL_IND\n", mmh->ref);
- switch(msg_type & GSM48_MMXX_MASK) {
- case GSM48_MMCC_CLASS:
- mmh->msg_type = GSM48_MMCC_REL_IND;
- break;
- case GSM48_MMSS_CLASS:
- mmh->msg_type = GSM48_MMSS_REL_IND;
- break;
- case GSM48_MMSMS_CLASS:
- mmh->msg_type = GSM48_MMSMS_REL_IND;
- break;
- }
- 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) */
@@ -3466,11 +3663,11 @@ static int gsm48_mm_release_active(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_mm_conn *conn;
/* get connection, if not exist (anymore), release */
- conn = mm_conn_by_ref(mm, mmh->ref);
+ conn = mm_conn_by_ref_and_class(mm, mmh->ref, (mmh->msg_type & GSM48_MMXX_MASK));
if (conn)
mm_conn_free(conn);
- /* state depends on the existance of remaining MM connections */
+ /* state depends on the existence of remaining MM connections */
if (llist_empty(&mm->mm_conn)) {
/* start RR release timer */
start_mm_t3240(mm);
@@ -3490,7 +3687,7 @@ static int gsm48_mm_release_wait_add(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_mm_conn *conn;
/* get connection, if not exist (anymore), release */
- conn = mm_conn_by_ref(mm, mmh->ref);
+ conn = mm_conn_by_ref_and_class(mm, mmh->ref, (mmh->msg_type & GSM48_MMXX_MASK));
if (conn)
mm_conn_free(conn);
@@ -3505,7 +3702,7 @@ static int gsm48_mm_release_wait_active(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_mm_conn *conn;
/* get connection, if not exist (anymore), release */
- conn = mm_conn_by_ref(mm, mmh->ref);
+ conn = mm_conn_by_ref_and_class(mm, mmh->ref, (mmh->msg_type & GSM48_MMXX_MASK));
if (conn)
mm_conn_free(conn);
@@ -3534,7 +3731,7 @@ static int gsm48_mm_release_wait_rr(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_mm_conn *conn;
/* get connection, if not exist (anymore), release */
- conn = mm_conn_by_ref(mm, mmh->ref);
+ conn = mm_conn_by_ref_and_class(mm, mmh->ref, (mmh->msg_type & GSM48_MMXX_MASK));
if (conn)
mm_conn_free(conn);
@@ -3562,6 +3759,351 @@ static int gsm48_mm_abort_rr(struct osmocom_ms *ms, struct msgb *msg)
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 gsm_settings *set = &ms->settings;
+ struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
+ struct msgb *nmsg;
+
+ if (mm->substate == GSM48_MM_SST_NO_IMSI && !set->asci_allow_any)
+ return gsm48_mm_group_reject(ms, msg);
+
+ 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 ||
+ mm->substate == GSM48_MM_SST_PLMN_SEARCH_NORMAL);
+
+ /* 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);
+
+ /* 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 gsm_subscriber *subscr = &ms->subscr;
+ 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) {
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, (subscr->sim_valid) ? GSM48_MM_SST_LIMITED_SERVICE
+ : GSM48_MM_SST_NO_IMSI);
+ }
+ if (mm->substate == GSM48_MM_SST_RX_VGCS_NORMAL)
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, 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 gsm_subscriber *subscr = &ms->subscr;
+ 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) {
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, (subscr->sim_valid) ? GSM48_MM_SST_LIMITED_SERVICE
+ : GSM48_MM_SST_NO_IMSI);
+ }
+ if (mm->substate == GSM48_MM_SST_RX_VGCS_NORMAL)
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, 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 gsm_settings *set = &ms->settings;
+ struct msgb *nmsg;
+
+ if (mm->substate != GSM48_MM_SST_RX_VGCS_NORMAL && !set->asci_allow_any)
+ return gsm48_mm_uplink_reject(ms, msg);
+
+ 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
*/
@@ -3640,19 +4182,51 @@ static struct downstate {
{SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE),
GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_no_rr},
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE),
+ GSM48_MMGCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+
+ {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) |
+ SBIT(GSM48_MM_SST_LOC_UPD_NEEDED),
+ GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_req},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) |
+ SBIT(GSM48_MM_SST_LOC_UPD_NEEDED),
+ 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},
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NO_IMSI),
+ GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_req},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NO_IMSI),
+ GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_req},
+
/* 4.2.2.5 PLMN search, normal service */
{SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr},
@@ -3663,10 +4237,54 @@ static struct downstate {
{SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_no_rr},
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
+ GSM48_MMGCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
+ GSM48_MMBCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
+ GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_req},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
+ GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_req},
+
/* 4.2.2.6 PLMN search */
{SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH),
GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH),
+ GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_req},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH),
+ GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_req},
+
+ /* 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_req},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_LIMITED),
+ GSM48_MMBCC_UPLINK_REQ, gsm48_mm_uplink_req},
+
/* 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},
@@ -3677,6 +4295,12 @@ static struct downstate {
{SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES,
GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_first},
+ {SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES,
+ GSM48_MMGCC_EST_REQ, gsm48_mm_init_mm_first},
+
+ {SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES,
+ GSM48_MMBCC_EST_REQ, gsm48_mm_init_mm_first},
+
{SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES,
GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_more},
@@ -3695,6 +4319,7 @@ static struct downstate {
{SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES,
GSM48_MMSMS_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},
@@ -3704,6 +4329,18 @@ static struct downstate {
{ALL_STATES, ALL_STATES,
GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_reject},
+ {ALL_STATES, ALL_STATES,
+ GSM48_MMGCC_EST_REQ, gsm48_mm_init_mm_reject},
+
+ {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,
@@ -3717,6 +4354,14 @@ static struct downstate {
SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
GSM48_MMSMS_DATA_REQ, gsm48_mm_data},
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) |
+ SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
+ GSM48_MMGCC_DATA_REQ, gsm48_mm_data},
+
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) |
+ SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
+ GSM48_MMBCC_DATA_REQ, gsm48_mm_data},
+
/* 4.5.2.1 MM Connection (REL) */
{SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES,
GSM48_MMCC_REL_REQ, gsm48_mm_release_active},
@@ -3727,6 +4372,12 @@ static struct downstate {
{SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES,
GSM48_MMSMS_REL_REQ, gsm48_mm_release_active},
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES,
+ GSM48_MMGCC_REL_REQ, gsm48_mm_release_active},
+
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES,
+ GSM48_MMBCC_REL_REQ, gsm48_mm_release_active},
+
{SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
GSM48_MMCC_REL_REQ, gsm48_mm_release_wait_add},
@@ -3736,6 +4387,12 @@ static struct downstate {
{SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
GSM48_MMSMS_REL_REQ, gsm48_mm_release_wait_add},
+ {SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
+ GSM48_MMGCC_REL_REQ, gsm48_mm_release_wait_add},
+
+ {SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
+ GSM48_MMBCC_REL_REQ, gsm48_mm_release_wait_add},
+
{SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN), ALL_STATES,
GSM48_MMCC_REL_REQ, gsm48_mm_release_wait_active},
@@ -3753,6 +4410,35 @@ static struct downstate {
{SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), ALL_STATES,
GSM48_MMSMS_REL_REQ, gsm48_mm_release_wait_rr},
+
+ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), ALL_STATES,
+ GSM48_MMGCC_REL_REQ, gsm48_mm_release_wait_rr},
+
+ {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 \
@@ -3767,7 +4453,7 @@ int gsm48_mmxx_downmsg(struct osmocom_ms *ms, struct msgb *msg)
int i, rc;
/* keep up to date with the transaction ID */
- conn = mm_conn_by_ref(mm, mmh->ref);
+ conn = mm_conn_by_ref_and_class(mm, mmh->ref, (mmh->msg_type & GSM48_MMXX_MASK));
if (conn)
conn->transaction_id = mmh->transaction_id;
@@ -3800,7 +4486,7 @@ int gsm48_mmxx_downmsg(struct osmocom_ms *ms, struct msgb *msg)
return rc;
}
-/* state trasitions for radio ressource messages (lower layer) */
+/* state trasitions for radio resource messages (lower layer) */
static struct rrdatastate {
uint32_t states;
int type;
@@ -3880,6 +4566,23 @@ static struct rrdatastate {
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},
@@ -3965,20 +4668,84 @@ static struct mmdatastate {
#define MMDATASLLEN \
(sizeof(mmdatastatelist) / sizeof(struct mmdatastate))
+static int create_conn_and_push_mm_hdr(struct gsm48_mmlayer *mm, struct msgb *msg, int rr_est, int rr_prim,
+ uint8_t sapi)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ uint8_t pdisc = gh->proto_discr & 0x0f;
+ uint8_t transaction_id;
+ uint32_t callref;
+ struct gsm48_mm_conn *conn;
+ struct gsm48_mmxx_hdr *mmh;
+
+ transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; /* flip */
+
+ 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);
+ return -EINVAL;
+ }
+ conn = mm_conn_new(mm, pdisc, transaction_id, sapi, mm_conn_new_ref++);
+ rr_prim = rr_est;
+ }
+ if (!conn)
+ return -ENOMEM;
+ 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 = callref;
+ mmh->transaction_id = transaction_id;
+ mmh->sapi = sapi;
+
+ /* go MM CONN ACTIVE state */
+ if (mm->state == GSM48_MM_ST_WAIT_NETWORK_CMD ||
+ mm->state == GSM48_MM_ST_RR_CONN_RELEASE_NA) {
+ /* stop RR release timer */
+ stop_mm_t3240(mm);
+
+ /* stop "RR connection release not allowed" timer */
+ stop_mm_t3241(mm);
+
+ new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0);
+ }
+
+ return 0;
+}
+
static int gsm48_mm_data_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;
uint8_t sapi = rrh->sapi;
- struct gsm48_hdr *gh = msgb_l3(msg);
- uint8_t pdisc = gh->proto_discr & 0x0f;
- uint8_t msg_type = gh->msg_type & 0xbf;
- struct gsm48_mmxx_hdr *mmh;
+ const struct gsm48_hdr *gh = msgb_l3(msg);
+ uint8_t pdisc, msg_type;
int msg_supported = 0; /* determine, if message is supported at all */
- int rr_prim = -1, rr_est = -1; /* no prim set */
uint8_t skip_ind;
int i, rc;
+ if (msgb_l3len(msg) < sizeof(*gh)) {
+ LOGP(DMM, LOGL_INFO, "%s(): short read of msgb: %s\n",
+ __func__, msgb_hexdump(msg));
+ return -EINVAL;
+ }
+
+ pdisc = gh->proto_discr & 0x0f;
+ msg_type = gh->msg_type & 0xbf;
+
/* 9.2.19 */
if (msg_type == GSM48_MT_MM_NULL) {
msgb_free(msg);
@@ -3994,61 +4761,6 @@ static int gsm48_mm_data_ind(struct osmocom_ms *ms, struct msgb *msg)
/* pull the RR header */
msgb_pull(msg, sizeof(struct gsm48_rr_hdr));
- /* create transaction (if not exists) and push message */
- switch (pdisc) {
- case GSM48_PDISC_CC:
- rr_prim = GSM48_MMCC_DATA_IND;
- rr_est = GSM48_MMCC_EST_IND;
- break;
- case GSM48_PDISC_NC_SS:
- rr_prim = GSM48_MMSS_DATA_IND;
- rr_est = GSM48_MMSS_EST_IND;
- break;
- case GSM48_PDISC_SMS:
- rr_prim = GSM48_MMSMS_DATA_IND;
- rr_est = GSM48_MMSMS_EST_IND;
- break;
- }
- if (rr_prim != -1) {
- uint8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4;
- /* flip */
- struct gsm48_mm_conn *conn;
-
- /* find transaction, if any */
- conn = mm_conn_by_id(mm, pdisc, transaction_id);
-
- /* create MM connection instance */
- if (!conn) {
- conn = mm_conn_new(mm, pdisc, transaction_id, sapi,
- mm_conn_new_ref++);
- rr_prim = rr_est;
- }
- if (!conn) {
- msgb_free(msg);
- return -ENOMEM;
- }
-
- /* 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;
-
- /* go MM CONN ACTIVE state */
- if (mm->state == GSM48_MM_ST_WAIT_NETWORK_CMD
- || mm->state == GSM48_MM_ST_RR_CONN_RELEASE_NA) {
- /* stop RR release timer */
- stop_mm_t3240(mm);
-
- /* stop "RR connection release not allowed" timer */
- stop_mm_t3241(mm);
-
- new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0);
- }
- }
-
/* forward message */
switch (pdisc) {
case GSM48_PDISC_MM:
@@ -4059,31 +4771,45 @@ static int gsm48_mm_data_ind(struct osmocom_ms *ms, struct msgb *msg)
msgb_free(msg);
return 0;
}
- break; /* follow the selection proceedure below */
+ break; /* follow the selection procedure below */
case GSM48_PDISC_CC:
- rc = gsm48_rcv_cc(ms, msg);
+ rc = create_conn_and_push_mm_hdr(mm, msg, GSM48_MMCC_EST_IND, GSM48_MMCC_DATA_IND, sapi);
+ if (rc == 0)
+ rc = gsm48_rcv_cc(ms, msg);
msgb_free(msg);
return rc;
case GSM48_PDISC_NC_SS:
- rc = gsm480_rcv_ss(ms, msg);
+ rc = create_conn_and_push_mm_hdr(mm, msg, GSM48_MMSS_EST_IND, GSM48_MMSS_DATA_IND, sapi);
+ if (rc == 0)
+ rc = gsm480_rcv_ss(ms, msg);
msgb_free(msg);
return rc;
case GSM48_PDISC_SMS:
- rc = gsm411_rcv_sms(ms, msg);
+ rc = create_conn_and_push_mm_hdr(mm, msg, GSM48_MMSMS_EST_IND, GSM48_MMSMS_DATA_IND, sapi);
+ if (rc == 0)
+ rc = gsm411_rcv_sms(ms, msg);
+ msgb_free(msg);
+ return rc;
+
+ case GSM48_PDISC_GROUP_CC:
+ rc = create_conn_and_push_mm_hdr(mm, msg, -1, GSM48_MMGCC_DATA_IND, sapi);
+ if (rc == 0)
+ rc = gsm44068_rcv_gcc_bcc(ms, msg);
+ msgb_free(msg);
+ return rc;
+ case GSM48_PDISC_BCAST_CC:
+ rc = create_conn_and_push_mm_hdr(mm, msg, -1, GSM48_MMBCC_DATA_IND, sapi);
+ if (rc == 0)
+ rc = gsm44068_rcv_gcc_bcc(ms, msg);
msgb_free(msg);
return rc;
- case 0x0f: /* test TS 04.14 */
- LOGP(DMM, LOGL_NOTICE, "Test protocol 0x%02x according to "
- "TS 04.14 is not supported.\n", pdisc);
- goto status;
default:
LOGP(DMM, LOGL_NOTICE, "Protocol type 0x%02x unsupported.\n",
- pdisc);
-status:
+ pdisc);
msgb_free(msg);
return gsm48_mm_tx_mm_status(ms,
GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED);
@@ -4165,6 +4891,10 @@ static struct eventstate {
{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),
@@ -4200,6 +4930,10 @@ static struct eventstate {
{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),
@@ -4235,12 +4969,15 @@ static struct eventstate {
{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},
@@ -4295,6 +5032,17 @@ static struct eventstate {
{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 \
@@ -4323,7 +5071,8 @@ static int gsm48_mm_ev(struct osmocom_ms *ms, int msg_type, struct msgb *msg)
&& ((1 << mm->substate) & eventstatelist[i].substates))
break;
if (i == EVENTSLLEN) {
- LOGP(DMM, LOGL_NOTICE, "Message unhandled at this state.\n");
+ LOGP(DMM, LOGL_NOTICE, "Message %s unhandled in state %s.\n",
+ get_mmevent_name(msg_type), gsm48_mm_state_names[mm->state]);
return 0;
}