LAPD: Add support for RTS based polling and T200

The T200 timer is started when the current frame is polled at
PH-READY-TO-SEND event.

A flag is used to enable this feature. The user of LAPD core must track
frame numbers to check the timeout condition. Then it must call the
external timeout function.

Related: OS#4074
Change-Id: Ib961b5a44911b99b0487641533301749c0286995
This commit is contained in:
Andreas Eversberg 2023-11-09 12:33:13 +01:00 committed by laforge
parent 0266b531c5
commit 49b7087360
4 changed files with 118 additions and 14 deletions

View File

@ -9,3 +9,4 @@
#library what description / commit summary line
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

View File

@ -84,6 +84,16 @@ enum lapd_state {
LAPD_STATE_TIMER_RECOV,
};
/*! lapd_flags */
#define LAPD_F_RTS 0x0001
/*! LAPD T200 state in RTS mode */
enum lapd_t200_rts {
LAPD_T200_RTS_OFF = 0,
LAPD_T200_RTS_PENDING,
LAPD_T200_RTS_RUNNING,
};
/*! LAPD message format (I / S / U) */
enum lapd_format {
LAPD_FORM_UKN = 0,
@ -133,6 +143,7 @@ struct lapd_datalink {
struct lapd_cr_ent rem2loc;
} cr;
enum lapd_mode mode; /*!< current mode of link */
unsigned int lapd_flags; /*!< \ref lapd_flags to change processing */
int use_sabme; /*!< use SABME instead of SABM */
int reestablish; /*!< enable reestablish support */
int n200, n200_est_rel; /*!< number of retranmissions */
@ -149,6 +160,7 @@ struct lapd_datalink {
uint8_t peer_busy; /*!< receiver busy on remote side */
int t200_sec, t200_usec; /*!< retry timer (default 1 sec) */
int t203_sec, t203_usec; /*!< retry timer (default 10 secs) */
enum lapd_t200_rts t200_rts; /*!< state of T200 in RTS mode */
struct osmo_timer_list t200; /*!< T200 timer */
struct osmo_timer_list t203; /*!< T203 timer */
uint8_t retrans_ctr; /*!< re-transmission counter */
@ -169,8 +181,11 @@ void lapd_dl_init2(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int max
void lapd_dl_set_name(struct lapd_datalink *dl, const char *name);
void lapd_dl_exit(struct lapd_datalink *dl);
void lapd_dl_reset(struct lapd_datalink *dl);
int lapd_dl_set_flags(struct lapd_datalink *dl, unsigned int flags);
int lapd_set_mode(struct lapd_datalink *dl, enum lapd_mode mode);
int lapd_ph_data_ind(struct msgb *msg, struct lapd_msg_ctx *lctx);
int lapd_ph_rts_ind(struct lapd_msg_ctx *lctx);
int lapd_recv_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx);
int lapd_t200_timeout(struct lapd_datalink *dl);
/*! @} */

View File

@ -204,11 +204,35 @@ static inline const char *lapd_state_name(enum lapd_state state)
static void lapd_start_t200(struct lapd_datalink *dl)
{
if (osmo_timer_pending(&dl->t200))
return;
LOGDL(dl, LOGL_INFO, "start T200 (timeout=%d.%06ds)\n",
dl->t200_sec, dl->t200_usec);
osmo_timer_schedule(&dl->t200, dl->t200_sec, dl->t200_usec);
if ((dl->lapd_flags & LAPD_F_RTS)) {
if (dl->t200_rts != LAPD_T200_RTS_OFF)
return;
LOGDL(dl, LOGL_INFO, "Start T200. (pending until triggered by RTS)\n");
dl->t200_rts = LAPD_T200_RTS_PENDING;
} else {
if (osmo_timer_pending(&dl->t200))
return;
LOGDL(dl, LOGL_INFO, "Start T200 (timeout=%d.%06ds).\n", dl->t200_sec, dl->t200_usec);
osmo_timer_schedule(&dl->t200, dl->t200_sec, dl->t200_usec);
}
}
/*! Handle timeout condition of T200 in RTS mode.
* The caller (LAPDm code) implements the T200 timer and must detect timeout condition.
* The function gets called by LAPDm code when it detects a timeout of T200.
* \param[in] dl caller-allocated datalink structure */
int lapd_t200_timeout(struct lapd_datalink *dl)
{
OSMO_ASSERT((dl->lapd_flags & LAPD_F_RTS));
if (dl->t200_rts != LAPD_T200_RTS_RUNNING)
return -EINVAL;
dl->t200_rts = LAPD_T200_RTS_OFF;
lapd_t200_cb(dl);
return 0;
}
static void lapd_start_t203(struct lapd_datalink *dl)
@ -221,10 +245,24 @@ static void lapd_start_t203(struct lapd_datalink *dl)
static void lapd_stop_t200(struct lapd_datalink *dl)
{
if (!osmo_timer_pending(&dl->t200))
return;
if ((dl->lapd_flags & LAPD_F_RTS)) {
if (dl->t200_rts == LAPD_T200_RTS_OFF)
return;
dl->t200_rts = LAPD_T200_RTS_OFF;
} else {
if (!osmo_timer_pending(&dl->t200))
return;
osmo_timer_del(&dl->t200);
}
LOGDL(dl, LOGL_INFO, "stop T200\n");
osmo_timer_del(&dl->t200);
}
static bool lapd_is_t200_started(struct lapd_datalink *dl)
{
if ((dl->lapd_flags & LAPD_F_RTS))
return (dl->t200_rts != LAPD_T200_RTS_OFF);
else
return osmo_timer_pending(&dl->t200);
}
static void lapd_stop_t203(struct lapd_datalink *dl)
@ -359,6 +397,21 @@ void lapd_dl_reset(struct lapd_datalink *dl)
lapd_dl_newstate(dl, LAPD_STATE_IDLE);
}
/*! Set lapd_flags to change behaviour
* \param[in] dl \ref lapd_datalink instance
* \param[in] flags \ref lapd_flags */
int lapd_dl_set_flags(struct lapd_datalink *dl, unsigned int flags)
{
if (lapd_is_t200_started(dl) && (flags & LAPD_F_RTS) != (dl->lapd_flags & LAPD_F_RTS)) {
LOGDL(dl, LOGL_ERROR, "Changing RTS flag not allowed while T200 is running.\n");
return -EINVAL;
}
dl->lapd_flags = flags;
return 0;
}
/* reset and de-allocate history buffer */
void lapd_dl_exit(struct lapd_datalink *dl)
{
@ -806,11 +859,8 @@ static void lapd_acknowledge(struct lapd_msg_ctx *lctx)
/* Stop T203, if running */
lapd_stop_t203(dl);
/* Start T203, if T200 is not running in MF EST state, if enabled */
if (!osmo_timer_pending(&dl->t200)
&& (dl->t203_sec || dl->t203_usec)
&& (dl->state == LAPD_STATE_MF_EST)) {
if (!lapd_is_t200_started(dl) && (dl->t203_sec || dl->t203_usec) && (dl->state == LAPD_STATE_MF_EST))
lapd_start_t203(dl);
}
}
/* L1 -> L2 */
@ -1732,6 +1782,31 @@ int lapd_ph_data_ind(struct msgb *msg, struct lapd_msg_ctx *lctx)
return rc;
}
/*! Enqueue next LAPD frame and run pending T200. (Must be called when frame is ready to send.)
* The caller (LAPDm code) calls this function before it sends the next frame.
* If there is no frame in the TX queue, LAPD will enqueue next I-frame, if possible.
* If the T200 is pending, it is changed to running state.
* \param[in] lctx LAPD context
* \param[out] rc set to 1, if timer T200 state changed to running, set to 0, if not. */
int lapd_ph_rts_ind(struct lapd_msg_ctx *lctx)
{
struct lapd_datalink *dl = lctx->dl;
/* If there is no pending frame, try to enqueue next I frame. */
if (llist_empty(&dl->tx_queue) && (dl->state == LAPD_STATE_MF_EST || dl->state == LAPD_STATE_TIMER_RECOV)) {
/* Send an I frame, if there are pending outgoing messages. */
lapd_send_i(dl, __LINE__, true);
}
/* Run T200 at RTS, if pending. Tell caller that is has been started. (rc = 1) */
if (dl->t200_rts == LAPD_T200_RTS_PENDING) {
dl->t200_rts = LAPD_T200_RTS_RUNNING;
return 1;
}
return 0;
}
/* L3 -> L2 */
/* send unit data */
@ -1861,6 +1936,12 @@ static int lapd_send_i(struct lapd_datalink *dl, int line, bool rts)
if (!rts)
LOGDL(dl, LOGL_INFO, "%s() called from line %d\n", __func__, line);
if ((dl->lapd_flags & LAPD_F_RTS) && !llist_empty(&dl->tx_queue)) {
if (!rts)
LOGDL(dl, LOGL_INFO, "There is a frame in the TX queue, not checking for sending I frame.\n");
return rc;
}
next_frame:
if (dl->peer_busy) {
@ -1978,7 +2059,7 @@ static int lapd_send_i(struct lapd_datalink *dl, int line, bool rts)
/* If timer T200 is not running at the time right before transmitting a
* frame, when the PH-READY-TO-SEND primitive is received from the
* physical layer., it shall be set. */
if (!osmo_timer_pending(&dl->t200)) {
if (!lapd_is_t200_started(dl)) {
/* stop Timer T203, if running */
lapd_stop_t203(dl);
/* start Timer T200 */
@ -1987,7 +2068,11 @@ static int lapd_send_i(struct lapd_datalink *dl, int line, bool rts)
dl->send_ph_data_req(&nctx, msg);
rc = 0; /* we sent something */
/* When using RTS, we send only one frame. */
if ((dl->lapd_flags & LAPD_F_RTS))
return 0;
rc = 0; /* We sent an I frame, so sending RR frame is not required. */
goto next_frame;
}

View File

@ -7,11 +7,14 @@ lapd_dl_init;
lapd_dl_init2;
lapd_dl_set_name;
lapd_dl_reset;
lapd_dl_set_flags;
lapd_msgb_alloc;
lapd_ph_data_ind;
lapd_ph_rts_ind;
lapd_recv_dlsap;
lapd_set_mode;
lapd_state_names;
lapd_t200_timeout;
osmo_i460_demux_in;
osmo_i460_mux_enqueue;