From 2d7119d85bb3226a5e70a4cfb48203611e782d68 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 9 Nov 2023 13:06:19 +0100 Subject: 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 --- TODO-RELEASE | 1 + include/osmocom/gsm/lapdm.h | 8 +++ src/gsm/lapdm.c | 155 ++++++++++++++++++++++++++++++++++++++++---- src/gsm/libosmogsm.map | 4 ++ 4 files changed, 157 insertions(+), 11 deletions(-) diff --git a/TODO-RELEASE b/TODO-RELEASE index 1d56d45c..226450b0 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 1a39fcaf..db666807 100644 --- a/include/osmocom/gsm/lapdm.h +++ b/include/osmocom/gsm/lapdm.h @@ -33,6 +33,9 @@ struct lapdm_datalink { 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_channel_reset(struct lapdm_channel *lc); 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 43f5662d..d6093171 100644 --- a/src/gsm/lapdm.c +++ b/src/gsm/lapdm.c @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -196,9 +197,9 @@ void lapdm_entity_init3(struct lapdm_entity *le, enum lapdm_mode mode, 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 @@ static int tx_ph_data_enqueue(struct lapdm_datalink *dl, struct msgb *msg, /* 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 @@ static int tx_ph_data_enqueue(struct lapdm_datalink *dl, struct msgb *msg, 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 @@ static struct msgb *tx_dequeue_acch_msgb(struct lapdm_entity *le) /* 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 @@ static struct msgb *tx_dequeue_acch_msgb(struct lapdm_entity *le) /*! 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 @@ int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp /* 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 @@ int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp 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 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le, 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 @@ void lapdm_channel_reset(struct lapdm_channel *lc) /*! 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 @@ void lapdm_channel_set_flags(struct lapdm_channel *lc, unsigned int flags) 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 056d3208..6bb3b4b7 100644 --- a/src/gsm/libosmogsm.map +++ b/src/gsm/libosmogsm.map @@ -587,7 +587,11 @@ lapdm_entity_init3; 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; -- cgit v1.2.3