pespin has submitted this change. ( 
https://gerrit.osmocom.org/c/libosmo-gprs/+/32050 )

Change subject: gmm: Initial implementation of GPRS Detach
......................................................................

gmm: Initial implementation of GPRS Detach

This patch contains further work on several areas for the GMM layer,
like fixes and improvements in existing primitives, initial FSM
implementation, initial Tx and Rx of some GMM messages, etc.

Related: OS#5501
Change-Id: If6cbb1d425b3a9f713348f1dea4747e2b6be0a44
---
M include/osmocom/gprs/gmm/gmm_ms_fsm.h
M include/osmocom/gprs/gmm/gmm_pdu.h
M include/osmocom/gprs/gmm/gmm_prim.h
M include/osmocom/gprs/gmm/gmm_private.h
M src/gmm/gmm.c
M src/gmm/gmm_ms_fsm.c
M src/gmm/gmm_pdu.c
M src/gmm/gmm_prim.c
M tests/gmm/gmm_prim_test.c
M tests/gmm/gmm_prim_test.err
M tests/gmm/gmm_prim_test.ok
11 files changed, 300 insertions(+), 34 deletions(-)

Approvals:
  osmith: Looks good to me, but someone else must approve
  Jenkins Builder: Verified
  pespin: Looks good to me, approved




diff --git a/include/osmocom/gprs/gmm/gmm_ms_fsm.h 
b/include/osmocom/gprs/gmm/gmm_ms_fsm.h
index 9639a9b..7aff7ab 100644
--- a/include/osmocom/gprs/gmm/gmm_ms_fsm.h
+++ b/include/osmocom/gprs/gmm/gmm_ms_fsm.h
@@ -1,6 +1,7 @@
 #pragma once

 #include <osmocom/core/fsm.h>
+#include <osmocom/gprs/gmm/gmm_prim.h>

 struct gprs_gmm_entity;

@@ -29,7 +30,8 @@
        GPRS_GMM_MS_EV_ATTACH_REQUESTED,
        GPRS_GMM_MS_EV_ATTACH_REJECTED,
        GPRS_GMM_MS_EV_ATTACH_ACCEPTED,
-       GPRS_GMM_MS_EV_DETACH_REQUESTED, /* poweroff or network initiated */
+       GPRS_GMM_MS_EV_DETACH_REQUESTED, /* also network initiated. data: ptr 
to enum osmo_gprs_gmm_detach_ms_type */
+       GPRS_GMM_MS_EV_DETACH_REQUESTED_POWEROFF,
        GPRS_GMM_MS_EV_DETACH_ACCEPTED,
        GPRS_GMM_MS_EV_SR_REQUESTED, /* (Iu only) */
        GPRS_GMM_MS_EV_SR_REJECTED, /* (Iu only) */
@@ -43,6 +45,8 @@
 struct gprs_gmm_ms_fsm_ctx {
        struct osmo_fsm_inst *fi;
        struct gprs_gmm_entity *gmme;
+       /* Type of last initiated detach: */
+       enum osmo_gprs_gmm_detach_ms_type detach_type;
 };

 int gprs_gmm_ms_fsm_init(void);
diff --git a/include/osmocom/gprs/gmm/gmm_pdu.h 
b/include/osmocom/gprs/gmm/gmm_pdu.h
index 5a9faa1..d005c0d 100644
--- a/include/osmocom/gprs/gmm/gmm_pdu.h
+++ b/include/osmocom/gprs/gmm/gmm_pdu.h
@@ -31,3 +31,8 @@

 int gprs_gmm_build_ciph_auth_resp(struct gprs_gmm_entity *gmme, bool 
imeisv_requested,
                                  uint8_t ac_ref_nr, const uint8_t sres[4], 
struct msgb *msg);
+
+int gprs_gmm_build_detach_req(struct gprs_gmm_entity *gmme,
+                             enum osmo_gprs_gmm_detach_ms_type detach_type,
+                             enum osmo_gprs_gmm_detach_poweroff_type 
poweroff_type,
+                             struct msgb *msg);
diff --git a/include/osmocom/gprs/gmm/gmm_prim.h 
b/include/osmocom/gprs/gmm/gmm_prim.h
index 6537075..a345651 100644
--- a/include/osmocom/gprs/gmm/gmm_prim.h
+++ b/include/osmocom/gprs/gmm/gmm_prim.h
@@ -56,6 +56,43 @@
        OSMO_GPRS_GMM_ATTACH_TYPE_EMERGENCY = 4,
        /* others: reserved, interpreted as OSMO_GPRS_GMM_ATTACH_TYPE_GPRS */
 };
+extern const struct value_string osmo_gprs_gmm_attach_type_names[];
+static inline const char *osmo_gprs_gmm_attach_ms_type_name(enum 
osmo_gprs_gmm_attach_type val)
+{
+       return get_value_string(osmo_gprs_gmm_attach_type_names, val);
+}
+
+/* Detach type 10.5.5.5 */
+enum osmo_gprs_gmm_detach_poweroff_type {
+       /* 0 is reserved */
+       OSMO_GPRS_GMM_DETACH_POWEROFF_TYPE_NORMAL = 0,
+       OSMO_GPRS_GMM_DETACH_POWEROFF_TYPE_POWEROFF = 1,
+};
+enum osmo_gprs_gmm_detach_ms_type {
+       /* 0 is reserved */
+       OSMO_GPRS_GMM_DETACH_MS_TYPE_GPRS = 1,
+       OSMO_GPRS_GMM_DETACH_MS_TYPE_IMSI = 2,
+       OSMO_GPRS_GMM_DETACH_MS_TYPE_COMBINED = 3,
+       /* others: reserved, interpreted as OSMO_GPRS_GMM_DETACH_TYPE_COMBINED 
*/
+};
+extern const struct value_string osmo_gprs_gmm_detach_ms_type_names[];
+static inline const char *osmo_gprs_gmm_detach_ms_type_name(enum 
osmo_gprs_gmm_detach_ms_type val)
+{
+       return get_value_string(osmo_gprs_gmm_detach_ms_type_names, val);
+}
+
+enum osmo_gprs_gmm_detach_network_type {
+       /* 0 is reserved */
+       OSMO_GPRS_GMM_DETACH_NETWORK_TYPE_REATTACH_REQUIRED = 1,
+       OSMO_GPRS_GMM_DETACH_NETWORK_TYPE_REATTACH_NOT_REQUIRED = 2,
+       OSMO_GPRS_GMM_DETACH_NETWORK_TYPE_IMSI = 3,
+       /* others: reserved, interpreted as 
OSMO_GPRS_GMM_DETACH_TYPE_REATTACH_NOT_REQUIRED */
+};
+extern const struct value_string osmo_gprs_gmm_detach_network_type_names[];
+static inline const char *osmo_gprs_gmm_detach_network_type_name(enum 
osmo_gprs_gmm_detach_network_type val)
+{
+       return get_value_string(osmo_gprs_gmm_detach_network_type_names, val);
+}

 /* Parameters for OSMO_GPRS_GMM_GMMREG_* prims */
 struct osmo_gprs_gmm_gmmreg_prim {
@@ -86,15 +123,17 @@
                } attach_cnf;
                /* OSMO_GPRS_GMM_GMMREG_DETACH | Req, 6.6.1.4 */
                struct {
-                       /* detach-type, power-off/normal-detach  */
+                       uint32_t ptmsi;
+                       enum osmo_gprs_gmm_detach_ms_type detach_type;
+                       enum osmo_gprs_gmm_detach_poweroff_type poweroff_type;
                } detach_req;
                /* OSMO_GPRS_GMM_GMMREG_DETACH | Cnf, 6.6.1.5 */
                struct {
-                       /* detach-type */
+                       enum osmo_gprs_gmm_detach_ms_type detach_type;
                } detach_cnf;
                /* OSMO_GPRS_GMM_GMMREG_DETACH | Ind, , 6.6.1.6 */
                struct {
-                       /* detach-type */
+                       enum osmo_gprs_gmm_detach_ms_type detach_type;
                } detach_ind;
        };
 };
@@ -197,6 +236,7 @@

 /* Alloc primitive for GMMREG SAP: */
 struct osmo_gprs_gmm_prim *osmo_gprs_gmm_prim_alloc_gmmreg_attach_req(void);
+struct osmo_gprs_gmm_prim *osmo_gprs_gmm_prim_alloc_gmmreg_detach_req(void);

 /* Alloc primitive for GMMRR SAP: */
 struct osmo_gprs_gmm_prim *osmo_gprs_gmm_prim_alloc_gmmrr_page_ind(uint32_t 
tlli);
diff --git a/include/osmocom/gprs/gmm/gmm_private.h 
b/include/osmocom/gprs/gmm/gmm_private.h
index 87b9843..0f05dff 100644
--- a/include/osmocom/gprs/gmm/gmm_private.h
+++ b/include/osmocom/gprs/gmm/gmm_private.h
@@ -17,6 +17,9 @@
 #include <osmocom/gprs/gmm/gmm_prim.h>
 #include <osmocom/gprs/gmm/gmm_ms_fsm.h>

+/* 3GPP TS 44.064 ยง 8.3 TLLI assignment procedures */
+#define GPRS_GMM_TLLI_UNASSIGNED (0xffffffff)
+
 extern int g_gmm_log_cat[_OSMO_GPRS_GMM_LOGC_MAX];

 #define LOGGMM(lvl, fmt, args...) LOGP(g_gmm_log_cat[OSMO_GPRS_GMM_LOGC_GMM], 
lvl, fmt, ## args)
@@ -86,6 +89,9 @@
 int gprs_gmm_tx_att_req(struct gprs_gmm_entity *gmme,
                        enum osmo_gprs_gmm_attach_type attach_type,
                        bool attach_with_imsi);
+int gprs_gmm_tx_detach_req(struct gprs_gmm_entity *gmme,
+                          enum osmo_gprs_gmm_detach_ms_type detach_type,
+                          enum osmo_gprs_gmm_detach_poweroff_type 
poweroff_type);

 #define LOGGMME(snme, level, fmt, args...) \
        LOGGMM(level, "GMME(PTMSI-%08x) " fmt, \
diff --git a/src/gmm/gmm.c b/src/gmm/gmm.c
index 4abf3a7..2ac6e97 100644
--- a/src/gmm/gmm.c
+++ b/src/gmm/gmm.c
@@ -179,6 +179,18 @@
        return rc;
 }

+static int gprs_gmm_submit_gmmreg_detach_cnf(struct gprs_gmm_entity *gmme)
+{
+       struct osmo_gprs_gmm_prim *gmm_prim_tx;
+       int rc;
+
+       gmm_prim_tx = gprs_gmm_prim_alloc_gmmreg_detach_cnf();
+       gmm_prim_tx->gmmreg.detach_cnf.detach_type = gmme->ms_fsm.detach_type;
+
+       rc = gprs_gmm_prim_call_up_cb(gmm_prim_tx);
+       return rc;
+}
+
 static int gprs_gmm_submit_gmmrr_assing_req(struct gprs_gmm_entity *gmme)
 {
        struct osmo_gprs_gmm_prim *gmm_prim_tx;
@@ -340,6 +352,39 @@
        return rc;
 }

+/* Tx GMM Detach Request (mobile originating detach), 9.4.5.2 */
+int gprs_gmm_tx_detach_req(struct gprs_gmm_entity *gmme,
+                          enum osmo_gprs_gmm_detach_ms_type detach_type,
+                          enum osmo_gprs_gmm_detach_poweroff_type 
poweroff_type)
+{
+       struct osmo_gprs_llc_prim *llc_prim;
+       int rc;
+       struct msgb *msg;
+
+       LOGGMME(gmme, LOGL_INFO, "Tx GMM DETACH REQUEST (MO)\n");
+       llc_prim = osmo_gprs_llc_prim_alloc_ll_unitdata_req(
+                       gmme->ptmsi, OSMO_GPRS_LLC_SAPI_GMM, NULL, 
GPRS_GMM_ALLOC_SIZE);
+       msg = llc_prim->oph.msg;
+       msg->l3h = msg->tail;
+       rc = gprs_gmm_build_detach_req(gmme, detach_type, poweroff_type, msg);
+       if (rc < 0) {
+               msgb_free(msg);
+               return -EBADMSG;
+       }
+       llc_prim->ll.l3_pdu = msg->l3h;
+       llc_prim->ll.l3_pdu_len = msgb_l3len(msg);
+       /* TODO:
+       llc_prim->ll.qos_params[3];
+       llc_prim->ll.radio_prio;
+       llc_prim->ll.apply_gea;
+       llc_prim->ll.apply_gia;
+       */
+
+       rc = gprs_gmm_prim_call_llc_down_cb(llc_prim);
+
+       return rc;
+}
+
 static int gprs_gmm_rx_att_ack(struct gprs_gmm_entity *gmme, struct gsm48_hdr 
*gh, unsigned int len)
 {
        struct gsm48_attach_ack *aa;
@@ -413,6 +458,41 @@
        return 0; /* TODO */
 }

+/* Rx GMM Detach Accept (mobile originating detach), 9.4.6.2 */
+static int gprs_gmm_rx_detach_accept(struct gprs_gmm_entity *gmme, struct 
gsm48_hdr *gh, unsigned int len)
+{
+       int rc;
+
+       if (len < sizeof(*gh) + 1) {
+               LOGGMME(gmme, LOGL_ERROR, "Rx GMM DETACH ACCEPT (MO) with wrong 
size %u\n", len);
+               goto rejected;
+       }
+
+       bool force_standby_indicated = (gh->data[0] >> 4) == 0x01;
+
+       LOGGMME(gmme, LOGL_DEBUG, "Rx GMM DETACH ACCEPT (MO) 
force_standby_indicated=%s\n",
+               force_standby_indicated ? "true" : "false");
+
+       /* TODO: submit GMMSM-RELEASE-IND */
+
+       /* Submit LLGMM-ASSIGN-REQ as per TS 24.007 Annex C.3 */
+       gmme->ptmsi = GPRS_GMM_TLLI_UNASSIGNED;
+       rc = gprs_gmm_submit_llgmm_assing_req(gmme);
+       if (rc < 0)
+               goto rejected;
+
+       /* Submit GMMREG-DETACH-CNF as per TS 24.007 Annex C.3 */
+       rc = gprs_gmm_submit_gmmreg_detach_cnf(gmme);
+       if (rc < 0)
+               goto rejected;
+
+       rc = osmo_fsm_inst_dispatch(gmme->ms_fsm.fi, 
GPRS_GMM_MS_EV_DETACH_ACCEPTED, NULL);
+       return rc;
+
+rejected:
+       return -EINVAL; /* TODO: what to do on error? */
+}
+
 /* Rx GMM Identity Request, 9.2.10 */
 static int gprs_gmm_rx_id_req(struct gprs_gmm_entity *gmme, struct gsm48_hdr 
*gh, unsigned int len)
 {
@@ -488,6 +568,9 @@
        case GSM48_MT_GMM_ATTACH_REJ:
                rc = gprs_gmm_rx_att_rej(gmme, gh, len);
                break;
+       case GSM48_MT_GMM_DETACH_ACK:
+               rc = gprs_gmm_rx_detach_accept(gmme, gh, len);
+               break;
        case GSM48_MT_GMM_ID_REQ:
                rc = gprs_gmm_rx_id_req(gmme, gh, len);
                break;
diff --git a/src/gmm/gmm_ms_fsm.c b/src/gmm/gmm_ms_fsm.c
index adb73a1..72c6d15 100644
--- a/src/gmm/gmm_ms_fsm.c
+++ b/src/gmm/gmm_ms_fsm.c
@@ -39,9 +39,6 @@
        case GPRS_GMM_MS_EV_ENABLE_GPRS_MODE:
                gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_DEREGISTERED);
                break;
-       case GPRS_GMM_MS_EV_DETACH_REQUESTED:
-               gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_DEREGISTERED);
-               break;
        default:
                OSMO_ASSERT(0);
        }
@@ -56,9 +53,6 @@
        case GPRS_GMM_MS_EV_ATTACH_REQUESTED:
                gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_REGISTERED_INITIATED);
                break;
-       case GPRS_GMM_MS_EV_DETACH_REQUESTED:
-               gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_DEREGISTERED);
-               break;
        default:
                OSMO_ASSERT(0);
        }
@@ -84,6 +78,7 @@

 static void st_gmm_ms_registered(struct osmo_fsm_inst *fi, uint32_t event, 
void *data)
 {
+       struct gprs_gmm_ms_fsm_ctx *ctx = (struct gprs_gmm_ms_fsm_ctx 
*)fi->priv;
        switch (event) {
        case GPRS_GMM_MS_EV_SR_REQUESTED:
                gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_REGISTERED);
@@ -92,6 +87,7 @@
                gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_RAU_INITIATED);
                break;
        case GPRS_GMM_MS_EV_DETACH_REQUESTED:
+               ctx->detach_type = *((enum osmo_gprs_gmm_detach_ms_type *)data);
                gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_DEREGISTERED_INITIATED);
                break;
        default:
@@ -109,9 +105,6 @@
        case GPRS_GMM_MS_EV_RAU_ACCEPTED:
                gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_REGISTERED);
                break;
-       case GPRS_GMM_MS_EV_DETACH_REQUESTED:
-               gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_DEREGISTERED);
-               break;
        default:
                OSMO_ASSERT(0);
        }
@@ -129,9 +122,6 @@
        case GPRS_GMM_MS_EV_RAU_ACCEPTED:
                gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_REGISTERED);
                break;
-       case GPRS_GMM_MS_EV_DETACH_REQUESTED:
-               gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_DEREGISTERED);
-               break;
        default:
                OSMO_ASSERT(0);
        }
@@ -146,7 +136,15 @@
        case GPRS_GMM_MS_EV_SR_ACCEPTED:
                gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_REGISTERED);
                break;
-       case GPRS_GMM_MS_EV_DETACH_REQUESTED:
+       default:
+               OSMO_ASSERT(0);
+       }
+}
+
+void gmm_ms_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void 
*data)
+{
+       switch (event) {
+       case GPRS_GMM_MS_EV_DETACH_REQUESTED_POWEROFF:
                gmm_ms_fsm_state_chg(fi, GPRS_GMM_MS_ST_DEREGISTERED);
                break;
        default:
@@ -157,8 +155,7 @@
 static struct osmo_fsm_state gmm_ms_fsm_states[] = {
        [GPRS_GMM_MS_ST_NULL] = {
                .in_event_mask =
-                       X(GPRS_GMM_MS_EV_ENABLE_GPRS_MODE)|
-                       X(GPRS_GMM_MS_EV_DETACH_REQUESTED),
+                       X(GPRS_GMM_MS_EV_ENABLE_GPRS_MODE),
                .out_state_mask =
                        X(GPRS_GMM_MS_ST_DEREGISTERED),
                .name = "Null",
@@ -167,8 +164,7 @@
        [GPRS_GMM_MS_ST_DEREGISTERED] = {
                .in_event_mask =
                        X(GPRS_GMM_MS_EV_DISABLE_GPRS_MODE) |
-                       X(GPRS_GMM_MS_EV_ATTACH_REQUESTED) |
-                       X(GPRS_GMM_MS_EV_DETACH_REQUESTED),
+                       X(GPRS_GMM_MS_EV_ATTACH_REQUESTED),
                .out_state_mask =
                        X(GPRS_GMM_MS_ST_NULL) |
                        X(GPRS_GMM_MS_ST_REGISTERED_INITIATED)|
@@ -192,7 +188,6 @@
                .in_event_mask =
                        X(GPRS_GMM_MS_EV_SR_REQUESTED) |
                        X(GPRS_GMM_MS_EV_RAU_REQUESTED) |
-                       X(GPRS_GMM_MS_EV_DETACH_REQUESTED) |
                        X(GPRS_GMM_MS_EV_DETACH_REQUESTED),
                .out_state_mask =
                        X(GPRS_GMM_MS_ST_REGISTERED) |
@@ -206,19 +201,17 @@
                .in_event_mask =
                        X(GPRS_GMM_MS_EV_DETACH_ACCEPTED) |
                        X(GPRS_GMM_MS_EV_LOW_LVL_FAIL) |
-                       X(GPRS_GMM_MS_EV_RAU_ACCEPTED) |
-                       X(GPRS_GMM_MS_EV_DETACH_REQUESTED),
+                       X(GPRS_GMM_MS_EV_RAU_ACCEPTED),
                .out_state_mask =
                        X(GPRS_GMM_MS_ST_REGISTERED) |
                        X(GPRS_GMM_MS_ST_DEREGISTERED),
                .name = "DeregisteredInitiated",
-               .action = st_gmm_ms_sr_initiated,
+               .action = st_gmm_ms_deregistered_initiated,
        },
        [GPRS_GMM_MS_ST_RAU_INITIATED] = {
                .in_event_mask =
                        X(GPRS_GMM_MS_EV_RAU_REJECTED) |
-                       X(GPRS_GMM_MS_EV_RAU_ACCEPTED) |
-                       X(GPRS_GMM_MS_EV_DETACH_REQUESTED),
+                       X(GPRS_GMM_MS_EV_RAU_ACCEPTED),
                .out_state_mask =
                        X(GPRS_GMM_MS_ST_REGISTERED) |
                        X(GPRS_GMM_MS_ST_DEREGISTERED_INITIATED) |
@@ -229,13 +222,12 @@
        [GPRS_GMM_MS_ST_SR_INITIATED] = {
                .in_event_mask =
                        X(GPRS_GMM_MS_EV_SR_REJECTED) |
-                       X(GPRS_GMM_MS_EV_SR_ACCEPTED) |
-                       X(GPRS_GMM_MS_EV_DETACH_REQUESTED),
+                       X(GPRS_GMM_MS_EV_SR_ACCEPTED),
                .out_state_mask =
                        X(GPRS_GMM_MS_ST_REGISTERED) |
                        X(GPRS_GMM_MS_ST_DEREGISTERED),
-               .name = "DeregisteredInitiated",
-               .action = st_gmm_ms_deregistered_initiated,
+               .name = "SRInitiated",
+               .action = st_gmm_ms_sr_initiated,
        },
 };

@@ -246,6 +238,7 @@
        { GPRS_GMM_MS_EV_ATTACH_REJECTED,       "ATTACH_REJECTED" },
        { GPRS_GMM_MS_EV_ATTACH_ACCEPTED,       "ATTACH_ACCEPTED" },
        { GPRS_GMM_MS_EV_DETACH_REQUESTED,      "DETACH_REQUESTED" },
+       { GPRS_GMM_MS_EV_DETACH_REQUESTED_POWEROFF, "DETACH_REQUESTED_POWEROFF" 
},
        { GPRS_GMM_MS_EV_DETACH_ACCEPTED,       "DETACH_ACCEPTED" },
        { GPRS_GMM_MS_EV_SR_REQUESTED,          "SR_REQUESTED" },
        { GPRS_GMM_MS_EV_SR_REJECTED,           "SR_REJECTED" },
@@ -267,6 +260,8 @@
        .states = gmm_ms_fsm_states,
        .num_states = ARRAY_SIZE(gmm_ms_fsm_states),
        .event_names = gmm_ms_fsm_event_names,
+       .allstate_event_mask = X(GPRS_GMM_MS_EV_DETACH_REQUESTED_POWEROFF),
+       .allstate_action = gmm_ms_fsm_allstate_action,
        .log_subsys = DLGLOBAL, /* updated dynamically through 
gprs_gmm_ms_fsm_set_log_cat() */
        .timer_cb = gmm_ms_fsm_timer_cb,
 };
diff --git a/src/gmm/gmm_pdu.c b/src/gmm/gmm_pdu.c
index 141e4a5..b8bea9d 100644
--- a/src/gmm/gmm_pdu.c
+++ b/src/gmm/gmm_pdu.c
@@ -328,3 +328,42 @@
        /* TODO: Optional IEs, eg Authentication parameter */
        return rc;
 }
+
+int gprs_gmm_build_detach_req(struct gprs_gmm_entity *gmme,
+                             enum osmo_gprs_gmm_detach_ms_type detach_type,
+                             enum osmo_gprs_gmm_detach_poweroff_type 
poweroff_type,
+                             struct msgb *msg)
+{
+       struct gsm48_hdr *gh;
+       uint8_t byte;
+       struct osmo_mobile_identity mi;
+       uint8_t *l;
+       int rc;
+
+       gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+       gh->proto_discr = GSM48_PDISC_MM_GPRS;
+       gh->msg_type = GSM48_MT_GMM_DETACH_REQ;
+
+       /* Detach type 10.5.5.5 + Spare half octet 10.5.1.8 */
+       byte = ((detach_type & 0x07) << 5) | (poweroff_type & 0x01) << 4;
+       msgb_put_u8(msg, byte);
+
+       /* DRX parameter 10.5.5.6 */
+       memcpy(msgb_put(msg, sizeof(drx_param_def)),
+              &drx_param_def,
+              sizeof(drx_param_def));
+
+       /* P-TMSI, Mobile identity 10.5.1.4 */
+       mi = (struct osmo_mobile_identity){
+               .type = GSM_MI_TYPE_TMSI,
+               .tmsi = gmme->ptmsi,
+       };
+       l = msgb_put(msg, 1); /* len */
+       rc = osmo_mobile_identity_encode_msgb(msg, &mi, false);
+       if (rc < 0)
+               return -EINVAL;
+       *l = rc;
+
+       /* TODO: optional fields: P-TMSI signature 10.5.5.8a */
+       return 0;
+}
diff --git a/src/gmm/gmm_prim.c b/src/gmm/gmm_prim.c
index c18d9d5..059c22a 100644
--- a/src/gmm/gmm_prim.c
+++ b/src/gmm/gmm_prim.c
@@ -65,6 +65,28 @@
        { 0, NULL }
 };

+const struct value_string osmo_gprs_gmm_attach_type_names[] = {
+       { OSMO_GPRS_GMM_ATTACH_TYPE_GPRS,       "GPRS attach" },
+       { OSMO_GPRS_GMM_ATTACH_TYPE_COMBINED_OLD, "Combined GPRS/IMSI attach 
(old protocol version)" },
+       { OSMO_GPRS_GMM_ATTACH_TYPE_COMBINED,   "Combined GPRS/IMSI attach" },
+       { OSMO_GPRS_GMM_ATTACH_TYPE_EMERGENCY,  "Emergency attach" },
+       { 0, NULL }
+};
+
+const struct value_string osmo_gprs_gmm_detach_ms_type_names[] = {
+       { OSMO_GPRS_GMM_DETACH_MS_TYPE_GPRS,    "GPRS detach" },
+       { OSMO_GPRS_GMM_DETACH_MS_TYPE_IMSI,    "IMSI detach" },
+       { OSMO_GPRS_GMM_DETACH_MS_TYPE_COMBINED, "Combined GPRS/IMSI detach" },
+       { 0, NULL }
+};
+
+const struct value_string osmo_gprs_gmm_detach_network_type_names[] = {
+       { OSMO_GPRS_GMM_DETACH_NETWORK_TYPE_REATTACH_REQUIRED,          
"Re-attach required" },
+       { OSMO_GPRS_GMM_DETACH_NETWORK_TYPE_REATTACH_NOT_REQUIRED,      
"Re-attach not required" },
+       { OSMO_GPRS_GMM_DETACH_NETWORK_TYPE_IMSI,                       "IMSI 
detach (after VLR failure)" },
+       { 0, NULL }
+};
+
 const char *osmo_gprs_gmm_prim_name(const struct osmo_gprs_gmm_prim *gmm_prim)
 {
        static char name_buf[256];
@@ -357,9 +379,31 @@
 static int gprs_gmm_prim_handle_gmmreg_detach_req(struct osmo_gprs_gmm_prim 
*gmm_prim)
 {
        int rc;
+       struct gprs_gmm_entity *gmme = 
gprs_gmm_find_gmme_by_tlli(gmm_prim->gmmreg.detach_req.ptmsi);

-       rc = gprs_gmm_prim_handle_unsupported(gmm_prim);
+       if (!gmme) {
+               LOGGMM(LOGL_ERROR, "Rx GMMREG-DETACH.req for unknown 
PTMSI=0x%08x\n",
+                      gmm_prim->gmmreg.detach_req.ptmsi);
+               return -EINVAL;
+       }

+       switch (gmm_prim->gmmreg.detach_req.poweroff_type) {
+       case OSMO_GPRS_GMM_DETACH_POWEROFF_TYPE_NORMAL:
+               /* C.3 MS initiated DETACH, GPRS only */
+               rc = osmo_fsm_inst_dispatch(gmme->ms_fsm.fi, 
GPRS_GMM_MS_EV_DETACH_REQUESTED,
+                                           
&gmm_prim->gmmreg.detach_req.detach_type);
+               break;
+       case OSMO_GPRS_GMM_DETACH_POWEROFF_TYPE_POWEROFF:
+               /* C.4 POWER-OFF DETACH, GPRS only */
+               rc = osmo_fsm_inst_dispatch(gmme->ms_fsm.fi, 
GPRS_GMM_MS_EV_DETACH_REQUESTED_POWEROFF, NULL);
+               break;
+       default:
+               OSMO_ASSERT(0);
+       }
+
+       rc = gprs_gmm_tx_detach_req(gmme,
+                                   gmm_prim->gmmreg.detach_req.detach_type,
+                                   gmm_prim->gmmreg.detach_req.poweroff_type);
        return rc;
 }

diff --git a/tests/gmm/gmm_prim_test.c b/tests/gmm/gmm_prim_test.c
index fdc50b7..70f8377 100644
--- a/tests/gmm/gmm_prim_test.c
+++ b/tests/gmm/gmm_prim_test.c
@@ -133,6 +133,10 @@
 0xea, 0x71, 0x1b, 0x41
 };

+static uint8_t pdu_gmm_detach_acc[] = {
+0x08, 0x06, 0x00
+};
+
 int test_gmm_prim_up_cb(struct osmo_gprs_gmm_prim *gmm_prim, void *user_data)
 {
        const char *pdu_name = osmo_gprs_gmm_prim_name(gmm_prim);
@@ -145,6 +149,10 @@
                               gmm_prim->gmmreg.attach_cnf.accepted,
                               gmm_prim->gmmreg.attach_cnf.rej.cause);
                        break;
+               case OSMO_PRIM(OSMO_GPRS_GMM_GMMREG_DETACH, PRIM_OP_CONFIRM):
+                       printf("%s(): Rx %s detach_type='%s'\n", __func__, 
pdu_name,
+                              
osmo_gprs_gmm_detach_ms_type_name(gmm_prim->gmmreg.detach_cnf.detach_type));
+                       break;
                default:
                        printf("%s(): Unexpected Rx %s\n", __func__, pdu_name);
                        OSMO_ASSERT(0)
@@ -217,13 +225,12 @@

        /* MS sends GMM Attach Req */
        gmm_prim = osmo_gprs_gmm_prim_alloc_gmmreg_attach_req();
+       OSMO_ASSERT(gmm_prim);
        gmm_prim->gmmreg.attach_req.attach_type = 
OSMO_GPRS_GMM_ATTACH_TYPE_GPRS;
        gmm_prim->gmmreg.attach_req.ptmsi = ptmsi;
        OSMO_STRLCPY_ARRAY(gmm_prim->gmmreg.attach_req.imsi, imsi);
        OSMO_STRLCPY_ARRAY(gmm_prim->gmmreg.attach_req.imei, imei);
        OSMO_STRLCPY_ARRAY(gmm_prim->gmmreg.attach_req.imeisv, imeisv);
-
-       OSMO_ASSERT(gmm_prim);
        rc = osmo_gprs_gmm_prim_upper_down(gmm_prim);
        OSMO_ASSERT(rc == 0);

@@ -248,6 +255,24 @@
        OSMO_ASSERT(rc == 0);
        /* As a result, MS answers GMM Attach Complete */

+       /* ... */
+
+       /* DETACH */
+       gmm_prim = osmo_gprs_gmm_prim_alloc_gmmreg_detach_req();
+       OSMO_ASSERT(gmm_prim);
+       gmm_prim->gmmreg.detach_req.detach_type = 
OSMO_GPRS_GMM_DETACH_MS_TYPE_GPRS;
+       gmm_prim->gmmreg.detach_req.poweroff_type = 
OSMO_GPRS_GMM_DETACH_POWEROFF_TYPE_NORMAL;
+       gmm_prim->gmmreg.detach_req.ptmsi = ptmsi;
+       rc = osmo_gprs_gmm_prim_upper_down(gmm_prim);
+       OSMO_ASSERT(rc == 0);
+
+       /* Network sends GMM Detach Accept */
+       llc_prim = gprs_llc_prim_alloc_ll_unitdata_ind(tlli, 
OSMO_GPRS_LLC_SAPI_GMM, (uint8_t *)pdu_gmm_detach_acc, 
sizeof(pdu_gmm_detach_acc));
+       OSMO_ASSERT(llc_prim);
+       rc = osmo_gprs_gmm_prim_llc_lower_up(llc_prim);
+       OSMO_ASSERT(rc == 0);
+       /* As a result, MS answers GMM Attach Complete */
+
        printf("==== %s() [end] ====\n", __func__);
 }

diff --git a/tests/gmm/gmm_prim_test.err b/tests/gmm/gmm_prim_test.err
index 0256edf..8e50caa 100644
--- a/tests/gmm/gmm_prim_test.err
+++ b/tests/gmm/gmm_prim_test.err
@@ -16,3 +16,11 @@
 DLGLOBAL INFO GMME(PTMSI-ea711b41) Tx GMM ATTACH COMPL
 DLGLOBAL INFO GMM_MS{RegisteredInitiated}: Received Event ATTACH_ACCEPTED
 DLGLOBAL INFO GMM_MS{RegisteredInitiated}: state_chg to Registered
+DLGLOBAL INFO Rx from upper layers: GMMREG-DETACH.request
+DLGLOBAL INFO GMM_MS{Registered}: Received Event DETACH_REQUESTED
+DLGLOBAL INFO GMM_MS{Registered}: state_chg to DeregisteredInitiated
+DLGLOBAL INFO GMME(PTMSI-ea711b41) Tx GMM DETACH REQUEST (MO)
+DLGLOBAL INFO Rx from lower layers: LL-UNITDATA.indication
+DLGLOBAL DEBUG GMME(PTMSI-ea711b41) Rx GMM DETACH ACCEPT (MO) 
force_standby_indicated=false
+DLGLOBAL INFO GMM_MS{DeregisteredInitiated}: Received Event DETACH_ACCEPTED
+DLGLOBAL INFO GMM_MS{DeregisteredInitiated}: state_chg to Deregistered
diff --git a/tests/gmm/gmm_prim_test.ok b/tests/gmm/gmm_prim_test.ok
index 91bcaeb..ed4f7e1 100644
--- a/tests/gmm/gmm_prim_test.ok
+++ b/tests/gmm/gmm_prim_test.ok
@@ -7,4 +7,7 @@
 test_gmm_prim_llc_down_cb(): Rx LLGM-ASSIGN.request TLLI=0x00000000
 test_gmm_prim_down_cb(): Rx GMRR-ASSIGN.request new_tlli=0xea711b41
 test_gmm_prim_llc_down_cb(): Rx LL-UNITDATA.request TLLI=0xea711b41 SAPI=GMM 
l3=[08 03 ]
+test_gmm_prim_llc_down_cb(): Rx LL-UNITDATA.request TLLI=0xea711b41 SAPI=GMM 
l3=[08 05 20 0a 00 05 f4 ea 71 1b 41 ]
+test_gmm_prim_llc_down_cb(): Rx LLGM-ASSIGN.request TLLI=0x00000000
+test_gmm_prim_up_cb(): Rx GMMREG-DETACH.confirm detach_type='GPRS detach'
 ==== test_gmm_prim_ms() [end] ====

--
To view, visit https://gerrit.osmocom.org/c/libosmo-gprs/+/32050
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings

Gerrit-Project: libosmo-gprs
Gerrit-Branch: master
Gerrit-Change-Id: If6cbb1d425b3a9f713348f1dea4747e2b6be0a44
Gerrit-Change-Number: 32050
Gerrit-PatchSet: 2
Gerrit-Owner: pespin <pes...@sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanits...@sysmocom.de>
Gerrit-Reviewer: laforge <lafo...@osmocom.org>
Gerrit-Reviewer: osmith <osm...@sysmocom.de>
Gerrit-Reviewer: pespin <pes...@sysmocom.de>
Gerrit-MessageType: merged

Reply via email to