jolly has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmocom-bb/+/34492?usp=email )
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, 700 insertions(+), 110 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/92/34492/1
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 4f47c9b..d0de235 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,13 @@ #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_MMBCC_EST_REQ 0x610 #define GSM48_MMBCC_EST_CNF 0x611 #define GSM48_MMBCC_REL_REQ 0x620 @@ -77,18 +84,32 @@ #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 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 */ @@ -233,6 +254,12 @@
/* sapi 3 */ int sapi3_link; + + /* VGCS additional states */ + bool vgcs; /* We are in group/broadcast mode. */ + bool vgcs_group_call; /* This is a group call. */ + uint32_t vgcs_callref; /* Callref of this call. */ + bool vgcs_normal_service; /* Service state before group transmit mode. */ };
/* MM connection entry */ diff --git a/src/host/layer23/src/mobile/gsm48_mm.c b/src/host/layer23/src/mobile/gsm48_mm.c index dc6e079..7b7cb59 100644 --- a/src/host/layer23/src/mobile/gsm48_mm.c +++ b/src/host/layer23/src/mobile/gsm48_mm.c @@ -144,6 +144,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 +179,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 +191,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 +269,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 independant 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. */
/* @@ -574,6 +647,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 } };
@@ -658,6 +733,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" }, @@ -669,17 +764,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 } };
@@ -911,8 +1004,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" @@ -928,8 +1021,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 */ @@ -1001,6 +1094,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; } }
@@ -1124,6 +1222,7 @@ struct gsm48_mmlayer *mm = &ms->mmlayer; struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = &cs->sel_si; + bool vgcs = mm->vgcs;
if (cs->state != GSM322_C3_CAMPED_NORMALLY && cs->state != GSM322_C7_CAMPED_ANY_CELL) { @@ -1174,7 +1273,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; } @@ -1186,13 +1286,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) @@ -1200,19 +1302,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; @@ -1239,6 +1344,7 @@ struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = &cs->sel_si; struct gsm_settings *set = &ms->settings; + bool vgcs = mm->vgcs;
/* no SIM is inserted */ if (!subscr->sim_valid) { @@ -1258,7 +1364,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); @@ -1273,7 +1380,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); @@ -1295,7 +1403,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); @@ -1316,7 +1425,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); @@ -1424,9 +1534,8 @@ /* new MM connection state */ static void new_conn_state(struct gsm48_mm_conn *conn, int 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]); + 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; }
@@ -2144,6 +2253,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; @@ -2275,6 +2386,7 @@ struct gsm_subscriber *subscr = &ms->subscr; struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = &cs->sel_si; + bool vgcs = mm->vgcs; struct msgb *nmsg;
/* in case we already have a location update going on */ @@ -2314,7 +2426,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 */ @@ -3396,6 +3509,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 */ @@ -3415,11 +3529,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); @@ -3463,27 +3579,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, NULL); + return gsm48_mm_to_rr(ms, msg, GSM48_RR_DATA_REQ, sapi, 0, NULL); }
/* release of MM connection (active state) */ @@ -3590,6 +3711,333 @@ 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 = 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; + + /* Push RR header and send to RR layer. */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_GROUP_REQ, 0, 0, &mmh->ch_desc); +} + +/* 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 = 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 = 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, NULL); +} + +/* 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, NULL); +} + +/* 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, NULL); +} + /* * other processes */ @@ -3674,15 +4122,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}, @@ -3707,6 +4173,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}, @@ -3733,10 +4225,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}, @@ -3747,12 +4236,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},
@@ -3768,6 +4252,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, @@ -3843,6 +4333,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 \ @@ -3970,6 +4483,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}, @@ -4063,8 +4593,6 @@ struct gsm48_hdr *gh = msgb_l3(msg); uint8_t pdisc = gh->proto_discr & 0x0f; uint8_t msg_type = gh->msg_type & 0xbf; - uint8_t transaction_id; - struct gsm48_mm_conn *conn; struct gsm48_mmxx_hdr *mmh; int msg_supported = 0; /* determine, if message is supported at all */ int rr_prim = -1, rr_est = -1; /* no prim set */ @@ -4086,7 +4614,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; @@ -4106,56 +4634,57 @@ case GSM48_PDISC_BCAST_CC: rr_prim = GSM48_MMBCC_DATA_IND; break; - default: - LOGP(DMM, LOGL_NOTICE, "Protocol type 0x%02x unsupported.\n", - pdisc); - msgb_free(msg); - return gsm48_mm_tx_mm_status(ms, - GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED); } + if (rr_prim != -1) { + uint8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; /* flip */ + uint32_t callref; + struct gsm48_mm_conn *conn;
- transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; /* flip */ + if (mm->vgcs) { + /* Ongoing group call. */ + callref = mm->vgcs_callref; + } else { + /* find transaction, if any */ + conn = mm_conn_by_id(mm, pdisc, transaction_id);
- /* 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; + } + if (!conn) { + msgb_free(msg); + return -ENOMEM; + } + callref = conn->ref; }
- conn = mm_conn_new(mm, pdisc, transaction_id, sapi, - mm_conn_new_ref++); - 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 = 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); } - rr_prim = rr_est; - } - - /* 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 */ @@ -4190,6 +4719,13 @@ rc = -ENOTSUP; msgb_free(msg); return rc; + + default: + LOGP(DMM, LOGL_NOTICE, "Protocol type 0x%02x unsupported.\n", + pdisc); + msgb_free(msg); + return gsm48_mm_tx_mm_status(ms, + GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED); }
LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' in MM state %s\n", ms->name, @@ -4268,6 +4804,9 @@ {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) */ + // nothing here + /* 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), @@ -4303,6 +4842,9 @@ {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) */ + // nothing here + /* 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), @@ -4398,6 +4940,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 \