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