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
This commit is contained in:
Andreas Eversberg 2023-11-09 13:06:19 +01:00 committed by laforge
parent 49b7087360
commit 2d7119d85b
4 changed files with 157 additions and 11 deletions

View File

@ -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

View File

@ -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);
/*! @} */

View File

@ -27,6 +27,7 @@
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <string.h>
#include <errno.h>
@ -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);
}
/*! @} */

View File

@ -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;