laforge has submitted this change. ( 
https://gerrit.osmocom.org/c/libosmocore/+/34986?usp=email )

Change subject: LAPDm: Add support for RTS based polling
......................................................................

LAPDm: Add support for RTS based polling

The lower layer must set the 'POLLING_ONLY' flag and provide frame
number when polling a frame. If T200 is pending, it is started with a
timeout frame number in advance to given frame number.

The lower layer must call lapdm_t200_fn() after a frame has been
received or if a frame has not been received. Also it must be called
after a TCH frame has been received. LAPDm uses this to check the T200
timeout condition.

A new function is used to set the frame number based timeout values.

Related: OS#4074
Change-Id: I6ebe83f829d7751ea9de1d90eb478c7a628db64c
---
M TODO-RELEASE
M include/osmocom/gsm/lapdm.h
M src/gsm/lapdm.c
M src/gsm/libosmogsm.map
4 files changed, 178 insertions(+), 11 deletions(-)

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




diff --git a/TODO-RELEASE b/TODO-RELEASE
index 1d56d45..226450b 100644
--- a/TODO-RELEASE
+++ b/TODO-RELEASE
@@ -10,3 +10,4 @@
 core      ADD       osmo_sock_multiaddr_{add,del}_local_addr()
 core      ADD       gsmtap_inst_fd2() core, DEPRECATE gsmtap_inst_fd()
 isdn           ABI change              add states and flags for external T200 
handling
+gsm            ABI change              add T200 timer states to lapdm_datalink
diff --git a/include/osmocom/gsm/lapdm.h b/include/osmocom/gsm/lapdm.h
index 1a39fca..db66680 100644
--- a/include/osmocom/gsm/lapdm.h
+++ b/include/osmocom/gsm/lapdm.h
@@ -33,6 +33,9 @@
        struct lapdm_msg_ctx mctx; /*!< context of established connection */

        struct lapdm_entity *entity; /*!< LAPDm entity we are part of */
+
+       uint32_t t200_fn;       /*!< T200 timer in frames */
+       uint32_t t200_timeout;  /*!< T200 timeout frame number */
 };

 /*! LAPDm datalink SAPIs */
@@ -119,6 +122,11 @@
 void lapdm_entity_set_flags(struct lapdm_entity *le, unsigned int flags);
 void lapdm_channel_set_flags(struct lapdm_channel *lc, unsigned int flags);

+void lapdm_entity_set_t200_fn(struct lapdm_entity *le, const uint32_t 
*t200_fn);
+void lapdm_channel_set_t200_fn(struct lapdm_channel *lc, const uint32_t 
*t200_fn_dcch, const uint32_t *t200_fn_acch);
+
 int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim 
*pp);
+int lapdm_phsap_dequeue_prim_fn(struct lapdm_entity *le, struct 
osmo_phsap_prim *pp, uint32_t fn);
+void lapdm_t200_fn(struct lapdm_entity *le, uint32_t fn);

 /*! @} */
diff --git a/src/gsm/lapdm.c b/src/gsm/lapdm.c
index 43f5662..d609317 100644
--- a/src/gsm/lapdm.c
+++ b/src/gsm/lapdm.c
@@ -27,6 +27,7 @@

 #include <stdio.h>
 #include <stdint.h>
+#include <inttypes.h>
 #include <string.h>
 #include <errno.h>

@@ -196,9 +197,9 @@
                char name[256];
                if (name_pfx) {
                        snprintf(name, sizeof(name), "%s[%s]", name_pfx, i == 0 
? "0" : "3");
-                       lapdm_dl_init(&le->datalink[i], le, t200_ms[i], n200, 
name);
+                       lapdm_dl_init(&le->datalink[i], le, (t200_ms) ? 
t200_ms[i] : 0, n200, name);
                } else
-                       lapdm_dl_init(&le->datalink[i], le, t200_ms[i], n200, 
NULL);
+                       lapdm_dl_init(&le->datalink[i], le, (t200_ms) ? 
t200_ms[i] : 0, n200, NULL);
        }

        lapdm_entity_set_mode(le, mode);
@@ -358,6 +359,18 @@

        /* if there is a pending message, queue it */
        if (le->tx_pending || le->flags & LAPDM_ENT_F_POLLING_ONLY) {
+               struct msgb *old_msg;
+
+               /* In 'Polling only' mode there can be only one message. */
+               if (le->flags & LAPDM_ENT_F_POLLING_ONLY) {
+                       /* Overwrite existing message by removing it first. */
+                       if ((old_msg = msgb_dequeue(&dl->dl.tx_queue))) {
+                               msgb_free(old_msg);
+                               /* Reset V(S) to V(A), because there is no 
outstanding message now. */
+                               dl->dl.v_send = dl->dl.v_ack;
+                       }
+               }
+
                *msgb_push(msg, 1) = pad;
                *msgb_push(msg, 1) = link_id;
                *msgb_push(msg, 1) = chan_nr;
@@ -377,21 +390,55 @@
        return le->l1_prim_cb(&pp.oph, le->l1_ctx);
 }

+/* Get transmit frame from queue, if any. In polling mode, indicate RTS to 
LAPD and start T200, if pending. */
+static struct msgb *tx_dequeue_msgb(struct lapdm_datalink *dl, uint32_t fn)
+{
+       struct msgb *msg;
+
+       /* Call RTS function of LAPD, to poll next frame. */
+       if (dl->entity->flags & LAPDM_ENT_F_POLLING_ONLY) {
+               struct lapd_msg_ctx lctx;
+               int rc;
+
+               /* Poll next frame. */
+               lctx.dl = &dl->dl;
+               rc = lapd_ph_rts_ind(&lctx);
+
+               /* If T200 has been started, calculate timeout FN. */
+               if (rc == 1) {
+                       /* Set T200 in advance. */
+                       dl->t200_timeout = fn;
+                       ADD_MODULO(dl->t200_timeout, dl->t200_fn, GSM_MAX_FN);
+
+                       LOGDL(&dl->dl, LOGL_INFO,
+                             "T200 running from FN %"PRIu32" to FN %"PRIu32" 
(%"PRIu32" frames).\n",
+                             fn, dl->t200_timeout, dl->t200_fn);
+               }
+       }
+
+       /* If there is no frame from LAPD, send UI frame, if any. */
+       msg = msgb_dequeue(&dl->dl.tx_queue);
+       if (msg)
+               LOGDL(&dl->dl, LOGL_INFO, "Sending frame from TX queue. (FN 
%"PRIu32")\n", fn);
+       return msg;
+}
+
 /* Dequeue a Downlink message for DCCH (dedicated channel) */
-static struct msgb *tx_dequeue_dcch_msgb(struct lapdm_entity *le)
+static struct msgb *tx_dequeue_dcch_msgb(struct lapdm_entity *le, uint32_t fn)
 {
        struct msgb *msg;

        /* SAPI=0 always has higher priority than SAPI=3 */
-       msg = msgb_dequeue(&le->datalink[DL_SAPI0].dl.tx_queue);
-       if (msg == NULL) /* no SAPI=0 messages, dequeue SAPI=3 (if any) */
-               msg = msgb_dequeue(&le->datalink[DL_SAPI3].dl.tx_queue);
+       msg = tx_dequeue_msgb(&le->datalink[DL_SAPI0], fn);
+       if (msg == NULL) { /* no SAPI=0 messages, dequeue SAPI=3 (if any) */
+               msg = tx_dequeue_msgb(&le->datalink[DL_SAPI3], fn);
+       }

        return msg;
 }

 /* Dequeue a Downlink message for ACCH (associated channel) */
-static struct msgb *tx_dequeue_acch_msgb(struct lapdm_entity *le)
+static struct msgb *tx_dequeue_acch_msgb(struct lapdm_entity *le, uint32_t fn)
 {
        struct lapdm_datalink *dl;
        int last = le->last_tx_dequeue;
@@ -403,7 +450,7 @@
                /* next */
                i = (i + 1) % n;
                dl = &le->datalink[i];
-               if ((msg = msgb_dequeue(&dl->dl.tx_queue)))
+               if ((msg = tx_dequeue_msgb(dl, fn)))
                        break;
        } while (i != last);

@@ -417,7 +464,7 @@

 /*! dequeue a msg that's pending transmission via L1 and wrap it into
  * a osmo_phsap_prim */
-int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim 
*pp)
+int lapdm_phsap_dequeue_prim_fn(struct lapdm_entity *le, struct 
osmo_phsap_prim *pp, uint32_t fn)
 {
        struct msgb *msg;
        uint8_t pad;
@@ -425,9 +472,9 @@
        /* Dequeue depending on channel type: DCCH or ACCH.
         * See 3GPP TS 44.005, section 4.2.2 "Priority". */
        if (le == &le->lapdm_ch->lapdm_dcch)
-               msg = tx_dequeue_dcch_msgb(le);
+               msg = tx_dequeue_dcch_msgb(le, fn);
        else
-               msg = tx_dequeue_acch_msgb(le);
+               msg = tx_dequeue_acch_msgb(le, fn);
        if (!msg)
                return -ENODEV;

@@ -449,6 +496,57 @@
        return 0;
 }

+static void lapdm_t200_fn_dl(struct lapdm_datalink *dl, uint32_t fn)
+{
+       uint32_t diff;
+
+       OSMO_ASSERT((dl->dl.lapd_flags & LAPD_F_RTS));
+
+       /* If T200 is running, check if it has fired. */
+       if (dl->dl.t200_rts != LAPD_T200_RTS_RUNNING)
+               return;
+
+       /* Calculate how many frames fn is behind t200_timeout.
+        * If it is negative (>= GSM_MAX_FN / 2), we have not reached 
t200_timeout yet.
+        * If it is 0 or positive, we reached it or we are a bit too late, 
which is not a problem.
+        */
+       diff = fn;
+       ADD_MODULO(diff, GSM_MAX_FN - dl->t200_timeout, GSM_MAX_FN);
+       if (diff >= GSM_MAX_FN / 2)
+               return;
+
+       LOGDL(&dl->dl, LOGL_INFO, "T200 timeout at FN %"PRIu32", detected at FN 
%"PRIu32".\n", dl->t200_timeout, fn);
+
+       lapd_t200_timeout(&dl->dl);
+}
+
+/*! Get receive frame number from L1. It is used to check the T200 timeout.
+ * This function is used if LAPD is in RTS mode only. (Applies if the 
LAPDM_ENT_F_POLLING_ONLY flag is set.)
+ * This function must be called for every valid or invalid data frame received.
+ * The frame number fn must be the frame number of the first burst of a data 
frame.
+ * This function must be called after the frame is delivered to layer 2.
+ * In case of TCH, this this function must be called for every speech frame 
received, meaning that there was no valid
+ * data frame. */
+void lapdm_t200_fn(struct lapdm_entity *le, uint32_t fn)
+{
+       unsigned int i;
+
+       if (!(le->flags & LAPDM_ENT_F_POLLING_ONLY)) {
+               LOGP(DLLAPD, LOGL_ERROR, "Function call not allowed on timer 
based T200.\n");
+               return;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(le->datalink); i++)
+               lapdm_t200_fn_dl(&le->datalink[i], fn);
+}
+
+/*! dequeue a msg that's pending transmission via L1 and wrap it into
+ * a osmo_phsap_prim */
+int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim 
*pp)
+{
+       return lapdm_phsap_dequeue_prim_fn(le, pp, 0);
+}
+
 /* get next frame from the tx queue. because the ms has multiple datalinks,
  * each datalink's queue is read round-robin.
  */
@@ -853,6 +951,7 @@
                        return -EINVAL;
                }
                /* send to LAPD */
+               LOGDL(lctx.dl, LOGL_DEBUG, "Frame received at FN %"PRIu32".\n", 
fn);
                rc = lapd_ph_data_ind(msg, &lctx);
                break;
        case LAPDm_FMT_Bter:
@@ -1494,7 +1593,20 @@
 /*! Set the flags of a LAPDm entity */
 void lapdm_entity_set_flags(struct lapdm_entity *le, unsigned int flags)
 {
+       unsigned int dl_flags = 0;
+       struct lapdm_datalink *dl;
+       int i;
+
        le->flags = flags;
+
+       /* Set flags at LAPD. */
+       if (le->flags & LAPDM_ENT_F_POLLING_ONLY)
+               dl_flags |= LAPD_F_RTS;
+
+       for (i = 0; i < ARRAY_SIZE(le->datalink); i++) {
+               dl = &le->datalink[i];
+               lapd_dl_set_flags(&dl->dl, dl_flags);
+       }
 }

 /*! Set the flags of all LAPDm entities in a LAPDm channel */
@@ -1504,4 +1616,25 @@
        lapdm_entity_set_flags(&lc->lapdm_acch, flags);
 }

+/*! Set the T200 FN timer of a LAPDm entity
+ *  \param[in] \ref lapdm_entity
+ *  \param[in] t200_fn Array of T200 timeout in frame numbers for all SAPIs 
(0, 3) */
+void lapdm_entity_set_t200_fn(struct lapdm_entity *le, const uint32_t *t200_fn)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(le->datalink); i++)
+               le->datalink[i].t200_fn = t200_fn[i];
+}
+
+/*! Set the T200 FN timer of all LAPDm entities in a LAPDm channel
+ *  \param[in] \ref lapdm_channel
+ *  \param[in] t200_fn_dcch Array of T200 timeout in frame numbers for all 
SAPIs (0, 3) on SDCCH/FACCH
+ *  \param[in] t200_fn_acch Array of T200 timeout in frame numbers for all 
SAPIs (0, 3) on SACCH */
+void lapdm_channel_set_t200_fn(struct lapdm_channel *lc, const uint32_t 
*t200_fn_dcch, const uint32_t *t200_fn_acch)
+{
+       lapdm_entity_set_t200_fn(&lc->lapdm_dcch, t200_fn_dcch);
+       lapdm_entity_set_t200_fn(&lc->lapdm_acch, t200_fn_acch);
+}
+
 /*! @} */
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index 056d320..6bb3b4b 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -587,7 +587,11 @@
 lapdm_entity_reset;
 lapdm_entity_set_flags;
 lapdm_entity_set_mode;
+lapdm_entity_set_t200_fn;
+lapdm_channel_set_t200_fn;
 lapdm_phsap_dequeue_prim;
+lapdm_phsap_dequeue_prim_fn;
+lapdm_t200_fn;
 lapdm_phsap_up;
 lapdm_rslms_recvmsg;


--
To view, visit https://gerrit.osmocom.org/c/libosmocore/+/34986?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings

Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Change-Id: I6ebe83f829d7751ea9de1d90eb478c7a628db64c
Gerrit-Change-Number: 34986
Gerrit-PatchSet: 17
Gerrit-Owner: jolly <[email protected]>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: daniel <[email protected]>
Gerrit-Reviewer: laforge <[email protected]>
Gerrit-Reviewer: neels <[email protected]>
Gerrit-MessageType: merged

Reply via email to