2017-06-20 02:35:06 +00:00
|
|
|
/*! \file lapdm.c
|
|
|
|
* GSM LAPDm (TS 04.06) implementation. */
|
|
|
|
/*
|
2019-06-02 19:33:38 +00:00
|
|
|
* (C) 2010-2019 by Harald Welte <laforge@gnumonks.org>
|
2011-09-26 09:23:06 +00:00
|
|
|
* (C) 2010-2011 by Andreas Eversberg <jolly@eversberg.eu>
|
2017-11-12 16:00:26 +00:00
|
|
|
* (C) 2014-2016 by sysmocom - s.f.m.c GmbH
|
2011-06-27 08:51:37 +00:00
|
|
|
*
|
|
|
|
* All Rights Reserved
|
|
|
|
*
|
2017-11-12 16:00:26 +00:00
|
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
|
|
*
|
2011-06-27 08:51:37 +00:00
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2011-08-17 16:22:08 +00:00
|
|
|
/*! \addtogroup lapdm
|
|
|
|
* @{
|
2017-06-20 02:35:06 +00:00
|
|
|
* \file lapdm.c */
|
2011-08-17 16:22:08 +00:00
|
|
|
|
2011-06-27 08:51:37 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include <osmocom/core/logging.h>
|
|
|
|
#include <osmocom/core/timer.h>
|
|
|
|
#include <osmocom/core/msgb.h>
|
|
|
|
#include <osmocom/core/utils.h>
|
|
|
|
|
|
|
|
#include <osmocom/gsm/tlv.h>
|
|
|
|
#include <osmocom/gsm/rsl.h>
|
|
|
|
#include <osmocom/gsm/prim.h>
|
|
|
|
#include <osmocom/gsm/gsm_utils.h>
|
|
|
|
#include <osmocom/gsm/lapdm.h>
|
|
|
|
|
|
|
|
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
|
|
|
#include <osmocom/gsm/protocol/gsm_08_58.h>
|
|
|
|
|
2018-05-01 16:11:02 +00:00
|
|
|
#define LAPD_U_SABM 0x7
|
|
|
|
|
2011-06-27 08:51:37 +00:00
|
|
|
/* TS 04.06 Figure 4 / Section 3.2 */
|
|
|
|
#define LAPDm_LPD_NORMAL 0
|
|
|
|
#define LAPDm_LPD_SMSCB 1
|
|
|
|
#define LAPDm_SAPI_NORMAL 0
|
|
|
|
#define LAPDm_SAPI_SMS 3
|
|
|
|
#define LAPDm_ADDR(lpd, sapi, cr) ((((lpd) & 0x3) << 5) | (((sapi) & 0x7) << 2) | (((cr) & 0x1) << 1) | 0x1)
|
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
#define LAPDm_ADDR_LPD(addr) (((addr) >> 5) & 0x3)
|
2011-06-27 08:51:37 +00:00
|
|
|
#define LAPDm_ADDR_SAPI(addr) (((addr) >> 2) & 0x7)
|
|
|
|
#define LAPDm_ADDR_CR(addr) (((addr) >> 1) & 0x1)
|
|
|
|
#define LAPDm_ADDR_EA(addr) ((addr) & 0x1)
|
|
|
|
|
|
|
|
/* TS 04.06 Table 3 / Section 3.4.3 */
|
|
|
|
#define LAPDm_CTRL_I(nr, ns, p) ((((nr) & 0x7) << 5) | (((p) & 0x1) << 4) | (((ns) & 0x7) << 1))
|
|
|
|
#define LAPDm_CTRL_S(nr, s, p) ((((nr) & 0x7) << 5) | (((p) & 0x1) << 4) | (((s) & 0x3) << 2) | 0x1)
|
|
|
|
#define LAPDm_CTRL_U(u, p) ((((u) & 0x1c) << (5-2)) | (((p) & 0x1) << 4) | (((u) & 0x3) << 2) | 0x3)
|
|
|
|
|
|
|
|
#define LAPDm_CTRL_is_I(ctrl) (((ctrl) & 0x1) == 0)
|
|
|
|
#define LAPDm_CTRL_is_S(ctrl) (((ctrl) & 0x3) == 1)
|
|
|
|
#define LAPDm_CTRL_is_U(ctrl) (((ctrl) & 0x3) == 3)
|
|
|
|
|
|
|
|
#define LAPDm_CTRL_U_BITS(ctrl) ((((ctrl) & 0xC) >> 2) | ((ctrl) & 0xE0) >> 3)
|
|
|
|
#define LAPDm_CTRL_PF_BIT(ctrl) (((ctrl) >> 4) & 0x1)
|
|
|
|
|
|
|
|
#define LAPDm_CTRL_S_BITS(ctrl) (((ctrl) & 0xC) >> 2)
|
|
|
|
|
|
|
|
#define LAPDm_CTRL_I_Ns(ctrl) (((ctrl) & 0xE) >> 1)
|
|
|
|
#define LAPDm_CTRL_Nr(ctrl) (((ctrl) & 0xE0) >> 5)
|
|
|
|
|
|
|
|
#define LAPDm_LEN(len) ((len << 2) | 0x1)
|
|
|
|
#define LAPDm_MORE 0x2
|
2011-09-26 09:23:06 +00:00
|
|
|
#define LAPDm_EL 0x1
|
|
|
|
|
|
|
|
#define LAPDm_U_UI 0x0
|
2011-06-27 08:51:37 +00:00
|
|
|
|
|
|
|
/* TS 04.06 Section 5.8.3 */
|
|
|
|
#define N201_AB_SACCH 18
|
|
|
|
#define N201_AB_SDCCH 20
|
|
|
|
#define N201_AB_FACCH 20
|
|
|
|
#define N201_Bbis 23
|
|
|
|
#define N201_Bter_SACCH 21
|
|
|
|
#define N201_Bter_SDCCH 23
|
|
|
|
#define N201_Bter_FACCH 23
|
|
|
|
#define N201_B4 19
|
|
|
|
|
|
|
|
/* 5.8.2.1 N200 during establish and release */
|
|
|
|
#define N200_EST_REL 5
|
|
|
|
/* 5.8.2.1 N200 during timer recovery state */
|
|
|
|
#define N200_TR_SACCH 5
|
|
|
|
#define N200_TR_SDCCH 23
|
|
|
|
#define N200_TR_FACCH_FR 34
|
|
|
|
#define N200_TR_EFACCH_FR 48
|
|
|
|
#define N200_TR_FACCH_HR 29
|
2011-09-26 09:23:06 +00:00
|
|
|
/* FIXME: set N200 depending on chan_nr */
|
|
|
|
#define N200 N200_TR_SDCCH
|
2011-06-27 08:51:37 +00:00
|
|
|
|
|
|
|
enum lapdm_format {
|
|
|
|
LAPDm_FMT_A,
|
|
|
|
LAPDm_FMT_B,
|
|
|
|
LAPDm_FMT_Bbis,
|
|
|
|
LAPDm_FMT_Bter,
|
|
|
|
LAPDm_FMT_B4,
|
|
|
|
};
|
|
|
|
|
2016-05-25 13:25:02 +00:00
|
|
|
const struct value_string osmo_ph_prim_names[] = {
|
|
|
|
{ PRIM_PH_DATA, "PH-DATA" },
|
|
|
|
{ PRIM_PH_RACH, "PH-RANDOM_ACCESS" },
|
|
|
|
{ PRIM_PH_CONN, "PH-CONNECT" },
|
|
|
|
{ PRIM_PH_EMPTY_FRAME, "PH-EMPTY_FRAME" },
|
|
|
|
{ PRIM_PH_RTS, "PH-RTS" },
|
|
|
|
{ PRIM_MPH_INFO, "MPH-INFO" },
|
|
|
|
{ PRIM_TCH, "TCH" },
|
|
|
|
{ PRIM_TCH_RTS, "TCH-RTS" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
2020-05-02 17:56:36 +00:00
|
|
|
extern void *tall_lapd_ctx;
|
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
static int lapdm_send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg);
|
|
|
|
static int send_rslms_dlsap(struct osmo_dlsap_prim *dp,
|
|
|
|
struct lapd_msg_ctx *lctx);
|
2014-03-20 18:24:48 +00:00
|
|
|
static int update_pending_frames(struct lapd_msg_ctx *lctx);
|
2011-06-27 08:51:37 +00:00
|
|
|
|
|
|
|
static void lapdm_dl_init(struct lapdm_datalink *dl,
|
2020-05-02 17:56:36 +00:00
|
|
|
struct lapdm_entity *entity, int t200_ms, uint32_t n200,
|
|
|
|
const char *name)
|
2011-06-27 08:51:37 +00:00
|
|
|
{
|
|
|
|
memset(dl, 0, sizeof(*dl));
|
|
|
|
dl->entity = entity;
|
2020-05-02 17:56:36 +00:00
|
|
|
lapd_dl_init2(&dl->dl, 1, 8, 251, name); /* Section 5.8.5 of TS 04.06 */
|
2011-09-26 09:23:06 +00:00
|
|
|
dl->dl.reestablish = 0; /* GSM uses no reestablish */
|
|
|
|
dl->dl.send_ph_data_req = lapdm_send_ph_data_req;
|
|
|
|
dl->dl.send_dlsap = send_rslms_dlsap;
|
2014-03-20 18:24:48 +00:00
|
|
|
dl->dl.update_pending_frames = update_pending_frames;
|
2011-09-26 09:23:06 +00:00
|
|
|
dl->dl.n200_est_rel = N200_EST_REL;
|
2019-06-02 19:33:38 +00:00
|
|
|
dl->dl.n200 = n200;
|
2011-09-26 09:23:06 +00:00
|
|
|
dl->dl.t203_sec = 0; dl->dl.t203_usec = 0;
|
2019-06-02 19:33:38 +00:00
|
|
|
dl->dl.t200_sec = t200_ms / 1000; dl->dl.t200_usec = (t200_ms % 1000) * 1000;
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! initialize a LAPDm entity and all datalinks inside
|
2011-08-17 16:22:08 +00:00
|
|
|
* \param[in] le LAPDm entity
|
|
|
|
* \param[in] mode \ref lapdm_mode (BTS/MS)
|
2019-06-02 19:33:38 +00:00
|
|
|
* \param[in] t200 T200 re-transmission timer for all SAPIs in seconds
|
|
|
|
*
|
|
|
|
* Don't use this function; It doesn't support different T200 values per API
|
|
|
|
* and doesn't permit the caller to specify the N200 counter, both of which
|
|
|
|
* are required by GSM specs and supported by lapdm_entity_init2().
|
2011-08-17 16:22:08 +00:00
|
|
|
*/
|
2011-11-06 19:35:48 +00:00
|
|
|
void lapdm_entity_init(struct lapdm_entity *le, enum lapdm_mode mode, int t200)
|
2019-06-02 19:33:38 +00:00
|
|
|
{
|
|
|
|
/* convert from single full-second value to per-SAPI milli-second value */
|
|
|
|
int t200_ms_sapi_arr[_NR_DL_SAPI];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(t200_ms_sapi_arr); i++)
|
|
|
|
t200_ms_sapi_arr[i] = t200 * 1000;
|
|
|
|
|
2020-05-02 17:56:36 +00:00
|
|
|
return lapdm_entity_init3(le, mode, t200_ms_sapi_arr, N200, NULL);
|
2019-06-02 19:33:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! initialize a LAPDm entity and all datalinks inside
|
|
|
|
* \param[in] le LAPDm entity
|
|
|
|
* \param[in] mode lapdm_mode (BTS/MS)
|
|
|
|
* \param[in] t200_ms per-SAPI array of T200 re-transmission timer in milli-seconds
|
|
|
|
* \param[in] n200 N200 re-transmisison count
|
|
|
|
*/
|
|
|
|
void lapdm_entity_init2(struct lapdm_entity *le, enum lapdm_mode mode,
|
|
|
|
const int *t200_ms, int n200)
|
2020-05-02 17:56:36 +00:00
|
|
|
{
|
|
|
|
lapdm_entity_init3(le, mode, t200_ms, n200, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! initialize a LAPDm entity and all datalinks inside
|
|
|
|
* \param[in] le LAPDm entity
|
|
|
|
* \param[in] mode lapdm_mode (BTS/MS)
|
|
|
|
* \param[in] t200_ms per-SAPI array of T200 re-transmission timer in milli-seconds
|
|
|
|
* \param[in] n200 N200 re-transmisison count
|
|
|
|
* \param[in] name human-readable name (will be copied internally + extended with SAPI)
|
|
|
|
*/
|
|
|
|
void lapdm_entity_init3(struct lapdm_entity *le, enum lapdm_mode mode,
|
|
|
|
const int *t200_ms, int n200, const char *name_pfx)
|
2011-06-27 08:51:37 +00:00
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
2020-05-02 17:56:36 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(le->datalink); i++) {
|
|
|
|
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);
|
|
|
|
} else
|
|
|
|
lapdm_dl_init(&le->datalink[i], le, t200_ms[i], n200, NULL);
|
|
|
|
}
|
2011-06-27 08:51:37 +00:00
|
|
|
|
|
|
|
lapdm_entity_set_mode(le, mode);
|
|
|
|
}
|
|
|
|
|
2019-06-02 19:33:38 +00:00
|
|
|
static int get_n200_dcch(enum gsm_chan_t chan_t)
|
|
|
|
{
|
|
|
|
switch (chan_t) {
|
|
|
|
case GSM_LCHAN_SDCCH:
|
|
|
|
return N200_TR_SDCCH;
|
|
|
|
case GSM_LCHAN_TCH_F:
|
|
|
|
return N200_TR_FACCH_FR;
|
|
|
|
case GSM_LCHAN_TCH_H:
|
|
|
|
return N200_TR_FACCH_HR;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! initialize a LAPDm channel and all its channels
|
2019-06-02 19:33:38 +00:00
|
|
|
* \param[in] lc lapdm_channel to be initialized
|
|
|
|
* \param[in] mode lapdm_mode (BTS/MS)
|
2011-08-17 16:22:08 +00:00
|
|
|
*
|
2019-06-02 19:33:38 +00:00
|
|
|
* Don't use this function; It doesn't support different T200 values per API
|
|
|
|
* and doesn't set the correct N200 counter, both of which
|
|
|
|
* are required by GSM specs and supported by lapdm_channel_init2().
|
2011-08-17 16:22:08 +00:00
|
|
|
*/
|
2011-06-27 08:51:37 +00:00
|
|
|
void lapdm_channel_init(struct lapdm_channel *lc, enum lapdm_mode mode)
|
|
|
|
{
|
2019-06-02 19:33:38 +00:00
|
|
|
/* emulate old backwards-compatible behavior with 1s/2s */
|
|
|
|
const int t200_ms_dcch[_NR_DL_SAPI] = { 1000, 1000 };
|
|
|
|
const int t200_ms_acch[_NR_DL_SAPI] = { 2000, 2000 };
|
|
|
|
|
2020-05-02 17:56:36 +00:00
|
|
|
lapdm_channel_init3(lc, mode, t200_ms_dcch, t200_ms_acch, GSM_LCHAN_SDCCH, NULL);
|
2019-06-02 19:33:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! initialize a LAPDm channel and all its channels
|
|
|
|
* \param[in] lc \ref lapdm_channel to be initialized
|
|
|
|
* \param[in] mode \ref lapdm_mode (BTS/MS)
|
|
|
|
* \param[in] t200_ms_dcch per-SAPI array of T200 in milli-seconds for DCCH
|
|
|
|
* \param[in] t200_ms_acch per-SAPI array of T200 in milli-seconds for SACCH
|
|
|
|
* \param[in] chan_t GSM channel type (to correctly set N200)
|
|
|
|
*/
|
|
|
|
int lapdm_channel_init2(struct lapdm_channel *lc, enum lapdm_mode mode,
|
|
|
|
const int *t200_ms_dcch, const int *t200_ms_acch, enum gsm_chan_t chan_t)
|
2020-05-02 17:56:36 +00:00
|
|
|
{
|
|
|
|
return lapdm_channel_init3(lc, mode, t200_ms_dcch, t200_ms_acch, chan_t, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! initialize a LAPDm channel and all its channels
|
|
|
|
* \param[in] lc \ref lapdm_channel to be initialized
|
|
|
|
* \param[in] mode \ref lapdm_mode (BTS/MS)
|
|
|
|
* \param[in] t200_ms_dcch per-SAPI array of T200 in milli-seconds for DCCH
|
|
|
|
* \param[in] t200_ms_acch per-SAPI array of T200 in milli-seconds for SACCH
|
|
|
|
* \param[in] chan_t GSM channel type (to correctly set N200)
|
|
|
|
* \parma[in] name_pfx human-readable name (copied by function + extended with ACCH/DCCH)
|
|
|
|
*/
|
|
|
|
int lapdm_channel_init3(struct lapdm_channel *lc, enum lapdm_mode mode,
|
|
|
|
const int *t200_ms_dcch, const int *t200_ms_acch, enum gsm_chan_t chan_t,
|
|
|
|
const char *name_pfx)
|
2019-06-02 19:33:38 +00:00
|
|
|
{
|
|
|
|
int n200_dcch = get_n200_dcch(chan_t);
|
2020-05-02 17:56:36 +00:00
|
|
|
char namebuf[256];
|
|
|
|
char *name = NULL;
|
|
|
|
|
2019-06-02 19:33:38 +00:00
|
|
|
if (n200_dcch < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2020-05-02 17:56:36 +00:00
|
|
|
osmo_talloc_replace_string(tall_lapd_ctx, &lc->name, name_pfx);
|
|
|
|
|
|
|
|
if (name_pfx) {
|
|
|
|
snprintf(namebuf, sizeof(namebuf), "%s[ACCH]", name_pfx);
|
|
|
|
name = namebuf;
|
|
|
|
}
|
|
|
|
lapdm_entity_init3(&lc->lapdm_acch, mode, t200_ms_acch, N200_TR_SACCH, name);
|
2018-05-04 18:58:48 +00:00
|
|
|
lc->lapdm_acch.lapdm_ch = lc;
|
2019-06-02 19:33:38 +00:00
|
|
|
|
2020-05-02 17:56:36 +00:00
|
|
|
if (name_pfx) {
|
|
|
|
snprintf(namebuf, sizeof(namebuf), "%s[DCCH]", name_pfx);
|
|
|
|
name = namebuf;
|
|
|
|
}
|
|
|
|
lapdm_entity_init3(&lc->lapdm_dcch, mode, t200_ms_dcch, n200_dcch, name);
|
2018-05-04 18:58:48 +00:00
|
|
|
lc->lapdm_dcch.lapdm_ch = lc;
|
2019-06-02 19:33:38 +00:00
|
|
|
|
|
|
|
return 0;
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! flush and release all resoures in LAPDm entity */
|
2011-06-27 08:51:37 +00:00
|
|
|
void lapdm_entity_exit(struct lapdm_entity *le)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
struct lapdm_datalink *dl;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(le->datalink); i++) {
|
|
|
|
dl = &le->datalink[i];
|
2011-09-26 09:23:06 +00:00
|
|
|
lapd_dl_exit(&dl->dl);
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/* lfush and release all resources in LAPDm channel
|
2011-08-17 16:22:08 +00:00
|
|
|
*
|
|
|
|
* A convenience wrapper calling \ref lapdm_entity_exit on both
|
|
|
|
* entities inside the \ref lapdm_channel
|
|
|
|
*/
|
2011-06-27 08:51:37 +00:00
|
|
|
void lapdm_channel_exit(struct lapdm_channel *lc)
|
|
|
|
{
|
|
|
|
lapdm_entity_exit(&lc->lapdm_acch);
|
|
|
|
lapdm_entity_exit(&lc->lapdm_dcch);
|
|
|
|
}
|
|
|
|
|
2014-03-26 12:45:17 +00:00
|
|
|
struct lapdm_datalink *lapdm_datalink_for_sapi(struct lapdm_entity *le, uint8_t sapi)
|
2011-06-27 08:51:37 +00:00
|
|
|
{
|
|
|
|
switch (sapi) {
|
|
|
|
case LAPDm_SAPI_NORMAL:
|
|
|
|
return &le->datalink[0];
|
|
|
|
case LAPDm_SAPI_SMS:
|
|
|
|
return &le->datalink[1];
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Append padding (if required) */
|
|
|
|
static void lapdm_pad_msgb(struct msgb *msg, uint8_t n201)
|
|
|
|
{
|
|
|
|
int pad_len = n201 - msgb_l2len(msg);
|
|
|
|
uint8_t *data;
|
|
|
|
|
|
|
|
if (pad_len < 0) {
|
2011-09-26 09:23:06 +00:00
|
|
|
LOGP(DLLAPD, LOGL_ERROR,
|
2011-06-27 08:51:37 +00:00
|
|
|
"cannot pad message that is already too big!\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-26 19:12:23 +00:00
|
|
|
data = msgb_put(msg, pad_len); /* TODO: random padding */
|
|
|
|
memset(data, GSM_MACBLOCK_PADDING, pad_len);
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* input function that L2 calls when sending messages up to L3 */
|
|
|
|
static int rslms_sendmsg(struct msgb *msg, struct lapdm_entity *le)
|
|
|
|
{
|
|
|
|
if (!le->l3_cb) {
|
|
|
|
msgb_free(msg);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* call the layer2 message handler that is registered */
|
|
|
|
return le->l3_cb(msg, le, le->l3_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* write a frame into the tx queue */
|
|
|
|
static int tx_ph_data_enqueue(struct lapdm_datalink *dl, struct msgb *msg,
|
2011-09-26 09:23:06 +00:00
|
|
|
uint8_t chan_nr, uint8_t link_id, uint8_t pad)
|
2011-06-27 08:51:37 +00:00
|
|
|
{
|
|
|
|
struct lapdm_entity *le = dl->entity;
|
|
|
|
struct osmo_phsap_prim pp;
|
|
|
|
|
|
|
|
/* if there is a pending message, queue it */
|
|
|
|
if (le->tx_pending || le->flags & LAPDM_ENT_F_POLLING_ONLY) {
|
2011-09-26 09:23:06 +00:00
|
|
|
*msgb_push(msg, 1) = pad;
|
2011-06-27 08:51:37 +00:00
|
|
|
*msgb_push(msg, 1) = link_id;
|
|
|
|
*msgb_push(msg, 1) = chan_nr;
|
2011-09-26 09:23:06 +00:00
|
|
|
msgb_enqueue(&dl->dl.tx_queue, msg);
|
2011-06-27 08:51:37 +00:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA,
|
|
|
|
PRIM_OP_REQUEST, msg);
|
|
|
|
pp.u.data.chan_nr = chan_nr;
|
|
|
|
pp.u.data.link_id = link_id;
|
|
|
|
|
|
|
|
/* send the frame now */
|
|
|
|
le->tx_pending = 0; /* disabled flow control */
|
2011-09-26 09:23:06 +00:00
|
|
|
lapdm_pad_msgb(msg, pad);
|
2011-06-27 08:51:37 +00:00
|
|
|
|
|
|
|
return le->l1_prim_cb(&pp.oph, le->l1_ctx);
|
|
|
|
}
|
|
|
|
|
lapdm: fix SAPI-0/SAPI-3 frame prioritization on DCCH
According to 3GPP TS 44.005, section 4.2.2 "Priority":
a) on DCCH, a SAPI=0 frame always has higher priority than SAPI=3;
b) on ACCH, the priority arrangement is more complex:
b1) if a SAPI = 3 frame is awaiting transmission, two SAPI=0
frames shall not be sent in consecutive SACCH frames;
b2) on the network side (LAPDM_MODE_BTS), it must also be ensured
that any SAPI=3 frame is followed by at least one SAPI=0 frame;
b3) a SAPI = 0 frame may be repeated in the next SACCH period
if the Repeated SACCH is supported (see 3GPP TS 44.006, section 11).
We definitely need to extend our testing coverage to ensure that
we implement b) correctly, but for now let's focus on DCCH:
a) for DCCH, ensure that SAPI=0 frames preceed SAPI=3 ones;
b) for ACCH, re-use the existing round-robin implementation.
Change-Id: Ia3780bce1222b312ae2fd2d21496a4d6c5ccb6e0
Related: SYS#5047, OS#4731
2020-08-27 13:58:49 +00:00
|
|
|
/* Dequeue a Downlink message for DCCH (dedicated channel) */
|
|
|
|
static struct msgb *tx_dequeue_dcch_msgb(struct lapdm_entity *le)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dequeue a Downlink message for ACCH (associated channel) */
|
|
|
|
static struct msgb *tx_dequeue_acch_msgb(struct lapdm_entity *le)
|
2011-06-27 08:51:37 +00:00
|
|
|
{
|
|
|
|
struct lapdm_datalink *dl;
|
|
|
|
int last = le->last_tx_dequeue;
|
|
|
|
int i = last, n = ARRAY_SIZE(le->datalink);
|
|
|
|
struct msgb *msg = NULL;
|
|
|
|
|
|
|
|
/* round-robin dequeue */
|
|
|
|
do {
|
|
|
|
/* next */
|
|
|
|
i = (i + 1) % n;
|
|
|
|
dl = &le->datalink[i];
|
2011-09-26 09:23:06 +00:00
|
|
|
if ((msg = msgb_dequeue(&dl->dl.tx_queue)))
|
2011-06-27 08:51:37 +00:00
|
|
|
break;
|
|
|
|
} while (i != last);
|
|
|
|
|
|
|
|
if (msg) {
|
|
|
|
/* Set last dequeue position */
|
|
|
|
le->last_tx_dequeue = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! dequeue a msg that's pending transmission via L1 and wrap it into
|
2011-06-27 08:51:37 +00:00
|
|
|
* a osmo_phsap_prim */
|
|
|
|
int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp)
|
|
|
|
{
|
|
|
|
struct msgb *msg;
|
2011-09-26 09:23:06 +00:00
|
|
|
uint8_t pad;
|
2011-06-27 08:51:37 +00:00
|
|
|
|
lapdm: fix SAPI-0/SAPI-3 frame prioritization on DCCH
According to 3GPP TS 44.005, section 4.2.2 "Priority":
a) on DCCH, a SAPI=0 frame always has higher priority than SAPI=3;
b) on ACCH, the priority arrangement is more complex:
b1) if a SAPI = 3 frame is awaiting transmission, two SAPI=0
frames shall not be sent in consecutive SACCH frames;
b2) on the network side (LAPDM_MODE_BTS), it must also be ensured
that any SAPI=3 frame is followed by at least one SAPI=0 frame;
b3) a SAPI = 0 frame may be repeated in the next SACCH period
if the Repeated SACCH is supported (see 3GPP TS 44.006, section 11).
We definitely need to extend our testing coverage to ensure that
we implement b) correctly, but for now let's focus on DCCH:
a) for DCCH, ensure that SAPI=0 frames preceed SAPI=3 ones;
b) for ACCH, re-use the existing round-robin implementation.
Change-Id: Ia3780bce1222b312ae2fd2d21496a4d6c5ccb6e0
Related: SYS#5047, OS#4731
2020-08-27 13:58:49 +00:00
|
|
|
/* 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);
|
|
|
|
else
|
|
|
|
msg = tx_dequeue_acch_msgb(le);
|
2011-06-27 08:51:37 +00:00
|
|
|
if (!msg)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
/* if we have a message, send PH-DATA.req */
|
|
|
|
osmo_prim_init(&pp->oph, SAP_GSM_PH, PRIM_PH_DATA,
|
|
|
|
PRIM_OP_REQUEST, msg);
|
|
|
|
|
|
|
|
/* Pull chan_nr and link_id */
|
|
|
|
pp->u.data.chan_nr = *msg->data;
|
|
|
|
msgb_pull(msg, 1);
|
|
|
|
pp->u.data.link_id = *msg->data;
|
|
|
|
msgb_pull(msg, 1);
|
2011-09-26 09:23:06 +00:00
|
|
|
pad = *msg->data;
|
2011-06-27 08:51:37 +00:00
|
|
|
msgb_pull(msg, 1);
|
|
|
|
|
|
|
|
/* Pad the frame, we can transmit now */
|
2011-09-26 09:23:06 +00:00
|
|
|
lapdm_pad_msgb(msg, pad);
|
2011-06-27 08:51:37 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get next frame from the tx queue. because the ms has multiple datalinks,
|
|
|
|
* each datalink's queue is read round-robin.
|
|
|
|
*/
|
|
|
|
static int l2_ph_data_conf(struct msgb *msg, struct lapdm_entity *le)
|
|
|
|
{
|
|
|
|
struct osmo_phsap_prim pp;
|
|
|
|
|
|
|
|
/* we may send again */
|
|
|
|
le->tx_pending = 0;
|
|
|
|
|
|
|
|
/* free confirm message */
|
|
|
|
if (msg)
|
|
|
|
msgb_free(msg);
|
|
|
|
|
|
|
|
if (lapdm_phsap_dequeue_prim(le, &pp) < 0) {
|
|
|
|
/* no message in all queues */
|
|
|
|
|
|
|
|
/* If user didn't request PH-EMPTY_FRAME.req, abort */
|
|
|
|
if (!(le->flags & LAPDM_ENT_F_EMPTY_FRAME))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* otherwise, send PH-EMPTY_FRAME.req */
|
|
|
|
osmo_prim_init(&pp.oph, SAP_GSM_PH,
|
|
|
|
PRIM_PH_EMPTY_FRAME,
|
|
|
|
PRIM_OP_REQUEST, NULL);
|
|
|
|
} else {
|
|
|
|
le->tx_pending = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return le->l1_prim_cb(&pp.oph, le->l1_ctx);
|
|
|
|
}
|
|
|
|
|
2018-04-19 14:11:14 +00:00
|
|
|
/* Is a given msg_type "transparent" as per TS 48.058 Section 8.1 */
|
|
|
|
static int rsl_is_transparent(uint8_t msg_type)
|
|
|
|
{
|
|
|
|
switch (msg_type) {
|
|
|
|
case RSL_MT_DATA_IND:
|
|
|
|
case RSL_MT_UNIT_DATA_IND:
|
|
|
|
return 1;
|
|
|
|
case RSL_MT_DATA_REQ:
|
|
|
|
case RSL_MT_UNIT_DATA_REQ:
|
|
|
|
return 1;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-27 08:51:37 +00:00
|
|
|
/* Create RSLms various RSLms messages */
|
|
|
|
static int send_rslms_rll_l3(uint8_t msg_type, struct lapdm_msg_ctx *mctx,
|
|
|
|
struct msgb *msg)
|
|
|
|
{
|
2018-04-19 14:11:14 +00:00
|
|
|
int transparent = rsl_is_transparent(msg_type);
|
|
|
|
|
2011-06-27 08:51:37 +00:00
|
|
|
/* Add the RSL + RLL header */
|
2018-04-19 14:11:14 +00:00
|
|
|
rsl_rll_push_l3(msg, msg_type, mctx->chan_nr, mctx->link_id, transparent);
|
2011-06-27 08:51:37 +00:00
|
|
|
|
|
|
|
/* send off the RSLms message to L3 */
|
|
|
|
return rslms_sendmsg(msg, mctx->dl->entity);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Take a B4 format message from L1 and create RSLms UNIT DATA IND */
|
|
|
|
static int send_rslms_rll_l3_ui(struct lapdm_msg_ctx *mctx, struct msgb *msg)
|
|
|
|
{
|
|
|
|
uint8_t l3_len = msg->tail - (uint8_t *)msgb_l3(msg);
|
|
|
|
|
|
|
|
/* Add the RSL + RLL header */
|
|
|
|
msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len);
|
|
|
|
|
2018-05-08 20:03:20 +00:00
|
|
|
/* Add two non-standard IEs carrying MS power and TA values for B4 (SACCH) */
|
|
|
|
if (mctx->lapdm_fmt == LAPDm_FMT_B4) {
|
|
|
|
msgb_tv_push(msg, RSL_IE_MS_POWER, mctx->tx_power_ind);
|
|
|
|
msgb_tv_push(msg, RSL_IE_TIMING_ADVANCE, mctx->ta_ind);
|
|
|
|
}
|
2011-06-27 08:51:37 +00:00
|
|
|
|
2018-05-08 19:53:28 +00:00
|
|
|
rsl_rll_push_hdr(msg, RSL_MT_UNIT_DATA_IND, mctx->chan_nr,
|
|
|
|
mctx->link_id, 1);
|
2017-12-08 13:30:47 +00:00
|
|
|
|
2011-06-27 08:51:37 +00:00
|
|
|
return rslms_sendmsg(msg, mctx->dl->entity);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int send_rll_simple(uint8_t msg_type, struct lapdm_msg_ctx *mctx)
|
|
|
|
{
|
|
|
|
struct msgb *msg;
|
2018-04-19 14:11:14 +00:00
|
|
|
int transparent = rsl_is_transparent(msg_type);
|
2011-06-27 08:51:37 +00:00
|
|
|
|
2018-04-19 14:11:14 +00:00
|
|
|
msg = rsl_rll_simple(msg_type, mctx->chan_nr, mctx->link_id, transparent);
|
2011-06-27 08:51:37 +00:00
|
|
|
|
|
|
|
/* send off the RSLms message to L3 */
|
|
|
|
return rslms_sendmsg(msg, mctx->dl->entity);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rsl_rll_error(uint8_t cause, struct lapdm_msg_ctx *mctx)
|
|
|
|
{
|
|
|
|
struct msgb *msg;
|
|
|
|
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGDL(&mctx->dl->dl, LOGL_NOTICE, "sending MDL-ERROR-IND %d\n", cause);
|
2018-04-19 14:11:14 +00:00
|
|
|
msg = rsl_rll_simple(RSL_MT_ERROR_IND, mctx->chan_nr, mctx->link_id, 0);
|
2011-06-27 08:51:37 +00:00
|
|
|
msgb_tlv_put(msg, RSL_IE_RLM_CAUSE, 1, &cause);
|
|
|
|
return rslms_sendmsg(msg, mctx->dl->entity);
|
|
|
|
}
|
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
/* DLSAP L2 -> L3 (RSLms) */
|
|
|
|
static int send_rslms_dlsap(struct osmo_dlsap_prim *dp,
|
|
|
|
struct lapd_msg_ctx *lctx)
|
2011-06-27 08:51:37 +00:00
|
|
|
{
|
2011-09-26 09:23:06 +00:00
|
|
|
struct lapd_datalink *dl = lctx->dl;
|
|
|
|
struct lapdm_datalink *mdl =
|
|
|
|
container_of(dl, struct lapdm_datalink, dl);
|
|
|
|
struct lapdm_msg_ctx *mctx = &mdl->mctx;
|
|
|
|
uint8_t rll_msg = 0;
|
|
|
|
|
2011-09-27 10:06:55 +00:00
|
|
|
switch (OSMO_PRIM_HDR(&dp->oph)) {
|
|
|
|
case OSMO_PRIM(PRIM_DL_EST, PRIM_OP_INDICATION):
|
|
|
|
rll_msg = RSL_MT_EST_IND;
|
2011-06-27 08:51:37 +00:00
|
|
|
break;
|
2011-09-27 10:06:55 +00:00
|
|
|
case OSMO_PRIM(PRIM_DL_EST, PRIM_OP_CONFIRM):
|
|
|
|
rll_msg = RSL_MT_EST_CONF;
|
2011-06-27 08:51:37 +00:00
|
|
|
break;
|
2011-09-27 10:06:55 +00:00
|
|
|
case OSMO_PRIM(PRIM_DL_DATA, PRIM_OP_INDICATION):
|
|
|
|
rll_msg = RSL_MT_DATA_IND;
|
2011-06-27 08:51:37 +00:00
|
|
|
break;
|
2011-09-27 10:06:55 +00:00
|
|
|
case OSMO_PRIM(PRIM_DL_UNIT_DATA, PRIM_OP_INDICATION):
|
|
|
|
return send_rslms_rll_l3_ui(mctx, dp->oph.msg);
|
|
|
|
case OSMO_PRIM(PRIM_DL_REL, PRIM_OP_INDICATION):
|
|
|
|
rll_msg = RSL_MT_REL_IND;
|
2011-06-27 08:51:37 +00:00
|
|
|
break;
|
2011-09-27 10:06:55 +00:00
|
|
|
case OSMO_PRIM(PRIM_DL_REL, PRIM_OP_CONFIRM):
|
|
|
|
rll_msg = RSL_MT_REL_CONF;
|
2011-06-27 08:51:37 +00:00
|
|
|
break;
|
2011-09-27 10:06:55 +00:00
|
|
|
case OSMO_PRIM(PRIM_DL_SUSP, PRIM_OP_CONFIRM):
|
|
|
|
rll_msg = RSL_MT_SUSP_CONF;
|
2011-06-27 08:51:37 +00:00
|
|
|
break;
|
2011-09-27 10:06:55 +00:00
|
|
|
case OSMO_PRIM(PRIM_MDL_ERROR, PRIM_OP_INDICATION):
|
|
|
|
rsl_rll_error(dp->u.error_ind.cause, mctx);
|
|
|
|
if (dp->oph.msg)
|
|
|
|
msgb_free(dp->oph.msg);
|
|
|
|
return 0;
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
if (!rll_msg) {
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGDL(dl, LOGL_ERROR, "Unsupported op %d, prim %d. Please "
|
2011-09-26 09:23:06 +00:00
|
|
|
"fix!\n", dp->oph.primitive, dp->oph.operation);
|
|
|
|
return -EINVAL;
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
if (!dp->oph.msg)
|
|
|
|
return send_rll_simple(rll_msg, mctx);
|
2011-06-27 08:51:37 +00:00
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
return send_rslms_rll_l3(rll_msg, mctx, dp->oph.msg);
|
|
|
|
}
|
2011-06-27 08:51:37 +00:00
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
/* send a data frame to layer 1 */
|
|
|
|
static int lapdm_send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg)
|
|
|
|
{
|
|
|
|
uint8_t l3_len = msg->tail - msg->data;
|
|
|
|
struct lapd_datalink *dl = lctx->dl;
|
|
|
|
struct lapdm_datalink *mdl =
|
|
|
|
container_of(dl, struct lapdm_datalink, dl);
|
|
|
|
struct lapdm_msg_ctx *mctx = &mdl->mctx;
|
|
|
|
int format = lctx->format;
|
|
|
|
|
|
|
|
/* prepend l2 header */
|
|
|
|
msg->l2h = msgb_push(msg, 3);
|
|
|
|
msg->l2h[0] = LAPDm_ADDR(lctx->lpd, lctx->sapi, lctx->cr);
|
|
|
|
/* EA is set here too */
|
|
|
|
switch (format) {
|
|
|
|
case LAPD_FORM_I:
|
|
|
|
msg->l2h[1] = LAPDm_CTRL_I(lctx->n_recv, lctx->n_send,
|
|
|
|
lctx->p_f);
|
2011-06-27 08:51:37 +00:00
|
|
|
break;
|
2011-09-26 09:23:06 +00:00
|
|
|
case LAPD_FORM_S:
|
|
|
|
msg->l2h[1] = LAPDm_CTRL_S(lctx->n_recv, lctx->s_u, lctx->p_f);
|
2011-06-27 08:51:37 +00:00
|
|
|
break;
|
2011-09-26 09:23:06 +00:00
|
|
|
case LAPD_FORM_U:
|
|
|
|
msg->l2h[1] = LAPDm_CTRL_U(lctx->s_u, lctx->p_f);
|
2011-06-27 08:51:37 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
msgb_free(msg);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2011-09-26 09:23:06 +00:00
|
|
|
msg->l2h[2] = LAPDm_LEN(l3_len); /* EL is set here too */
|
|
|
|
if (lctx->more)
|
|
|
|
msg->l2h[2] |= LAPDm_MORE;
|
2011-06-27 08:51:37 +00:00
|
|
|
|
2011-11-06 19:45:29 +00:00
|
|
|
/* add ACCH header with last indicated tx-power and TA */
|
|
|
|
if ((mctx->link_id & 0x40)) {
|
|
|
|
struct lapdm_entity *le = mdl->entity;
|
|
|
|
|
|
|
|
msg->l2h = msgb_push(msg, 2);
|
|
|
|
msg->l2h[0] = le->tx_power;
|
|
|
|
msg->l2h[1] = le->ta;
|
|
|
|
}
|
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id,
|
|
|
|
23);
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
|
2014-03-20 18:24:48 +00:00
|
|
|
static int update_pending_frames(struct lapd_msg_ctx *lctx)
|
|
|
|
{
|
|
|
|
struct lapd_datalink *dl = lctx->dl;
|
|
|
|
struct msgb *msg;
|
|
|
|
int rc = -1;
|
|
|
|
|
|
|
|
llist_for_each_entry(msg, &dl->tx_queue, list) {
|
|
|
|
if (LAPDm_CTRL_is_I(msg->l2h[1])) {
|
|
|
|
msg->l2h[1] = LAPDm_CTRL_I(dl->v_recv, LAPDm_CTRL_I_Ns(msg->l2h[1]),
|
|
|
|
LAPDm_CTRL_PF_BIT(msg->l2h[1]));
|
|
|
|
rc = 0;
|
|
|
|
} else if (LAPDm_CTRL_is_S(msg->l2h[1])) {
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGDL(dl, LOGL_ERROR, "Supervisory frame in queue, this shouldn't happen\n");
|
2014-03-20 18:24:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2018-05-01 16:11:02 +00:00
|
|
|
/* determine if receiving a given LAPDm message is not permitted */
|
|
|
|
static int lapdm_rx_not_permitted(const struct lapdm_entity *le,
|
|
|
|
const struct lapd_msg_ctx *lctx)
|
|
|
|
{
|
|
|
|
/* we currently only implement SABM related checks here */
|
|
|
|
if (lctx->format != LAPD_FORM_U || lctx->s_u != LAPD_U_SABM)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (le->mode == LAPDM_MODE_BTS) {
|
|
|
|
if (le == &le->lapdm_ch->lapdm_acch) {
|
|
|
|
/* no contention resolution on SACCH */
|
|
|
|
if (lctx->length > 0)
|
|
|
|
return RLL_CAUSE_SABM_INFO_NOTALL;
|
|
|
|
} else {
|
|
|
|
switch (lctx->sapi) {
|
|
|
|
case 3:
|
|
|
|
/* SAPI3 doesn't support contention resolution */
|
|
|
|
if (lctx->length > 0)
|
|
|
|
return RLL_CAUSE_SABM_INFO_NOTALL;
|
|
|
|
break;
|
2018-05-09 14:31:16 +00:00
|
|
|
default:
|
|
|
|
break;
|
2018-05-01 16:11:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (le->mode == LAPDM_MODE_MS) {
|
|
|
|
/* contention resolution (L3 present) is only sent by MS, but
|
|
|
|
* never received by it */
|
|
|
|
if (lctx->length > 0)
|
|
|
|
return RLL_CAUSE_SABM_INFO_NOTALL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-06-27 08:51:37 +00:00
|
|
|
/* input into layer2 (from layer 1) */
|
2022-10-09 16:49:19 +00:00
|
|
|
static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le, uint32_t fn,
|
2011-09-26 09:23:06 +00:00
|
|
|
uint8_t chan_nr, uint8_t link_id)
|
2011-06-27 08:51:37 +00:00
|
|
|
{
|
|
|
|
uint8_t cbits = chan_nr >> 3;
|
2011-06-27 21:32:14 +00:00
|
|
|
uint8_t sapi; /* we cannot take SAPI from link_id, as L1 has no clue */
|
2011-06-27 08:51:37 +00:00
|
|
|
struct lapdm_msg_ctx mctx;
|
2011-09-26 09:23:06 +00:00
|
|
|
struct lapd_msg_ctx lctx;
|
2011-06-27 08:51:37 +00:00
|
|
|
int rc = 0;
|
2011-09-26 09:23:06 +00:00
|
|
|
int n201;
|
2011-06-27 08:51:37 +00:00
|
|
|
|
|
|
|
/* when we reach here, we have a msgb with l2h pointing to the raw
|
|
|
|
* 23byte mac block. The l1h has already been purged. */
|
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
memset(&mctx, 0, sizeof(mctx));
|
2011-06-27 08:51:37 +00:00
|
|
|
mctx.chan_nr = chan_nr;
|
|
|
|
mctx.link_id = link_id;
|
|
|
|
|
|
|
|
/* check for L1 chan_nr/link_id and determine LAPDm hdr format */
|
|
|
|
if (cbits == 0x10 || cbits == 0x12) {
|
|
|
|
/* Format Bbis is used on BCCH and CCCH(PCH, NCH and AGCH) */
|
|
|
|
mctx.lapdm_fmt = LAPDm_FMT_Bbis;
|
2011-09-26 09:23:06 +00:00
|
|
|
n201 = N201_Bbis;
|
2011-06-27 21:32:14 +00:00
|
|
|
sapi = 0;
|
2011-06-27 08:51:37 +00:00
|
|
|
} else {
|
|
|
|
if (mctx.link_id & 0x40) {
|
2011-06-29 10:13:51 +00:00
|
|
|
/* It was received from network on SACCH */
|
|
|
|
|
2011-11-06 19:46:30 +00:00
|
|
|
/* If UI on SACCH sent by BTS, lapdm_fmt must be B4 */
|
|
|
|
if (le->mode == LAPDM_MODE_MS
|
|
|
|
&& LAPDm_CTRL_is_U(msg->l2h[3])
|
|
|
|
&& LAPDm_CTRL_U_BITS(msg->l2h[3]) == 0) {
|
2011-06-29 10:13:51 +00:00
|
|
|
mctx.lapdm_fmt = LAPDm_FMT_B4;
|
2011-09-26 09:23:06 +00:00
|
|
|
n201 = N201_B4;
|
2011-06-29 10:13:51 +00:00
|
|
|
} else {
|
|
|
|
mctx.lapdm_fmt = LAPDm_FMT_B;
|
2011-09-26 09:23:06 +00:00
|
|
|
n201 = N201_AB_SACCH;
|
2011-06-29 10:13:51 +00:00
|
|
|
}
|
2011-06-27 08:51:37 +00:00
|
|
|
/* SACCH frames have a two-byte L1 header that
|
|
|
|
* OsmocomBB L1 doesn't strip */
|
|
|
|
mctx.tx_power_ind = msg->l2h[0] & 0x1f;
|
|
|
|
mctx.ta_ind = msg->l2h[1];
|
|
|
|
msgb_pull(msg, 2);
|
|
|
|
msg->l2h += 2;
|
2011-06-27 21:32:14 +00:00
|
|
|
sapi = (msg->l2h[0] >> 2) & 7;
|
2011-06-27 08:51:37 +00:00
|
|
|
} else {
|
|
|
|
mctx.lapdm_fmt = LAPDm_FMT_B;
|
2011-11-06 19:43:08 +00:00
|
|
|
n201 = N201_AB_SDCCH;
|
2011-06-27 21:32:14 +00:00
|
|
|
sapi = (msg->l2h[0] >> 2) & 7;
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-26 12:45:17 +00:00
|
|
|
mctx.dl = lapdm_datalink_for_sapi(le, sapi);
|
2011-06-27 21:32:14 +00:00
|
|
|
/* G.2.1 No action on frames containing an unallocated SAPI. */
|
|
|
|
if (!mctx.dl) {
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGP(DLLAPD, LOGL_NOTICE, "Received frame for unsupported SAPI %d!\n", sapi);
|
2011-06-27 21:32:14 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2011-06-27 08:51:37 +00:00
|
|
|
switch (mctx.lapdm_fmt) {
|
|
|
|
case LAPDm_FMT_A:
|
|
|
|
case LAPDm_FMT_B:
|
|
|
|
case LAPDm_FMT_B4:
|
2011-09-26 09:23:06 +00:00
|
|
|
lctx.dl = &mctx.dl->dl;
|
2022-10-09 16:49:19 +00:00
|
|
|
lctx.dl->t200_fn = fn;
|
2011-09-26 09:23:06 +00:00
|
|
|
/* obtain SAPI from address field */
|
|
|
|
mctx.link_id |= LAPDm_ADDR_SAPI(msg->l2h[0]);
|
|
|
|
/* G.2.3 EA bit set to "0" is not allowed in GSM */
|
|
|
|
if (!LAPDm_ADDR_EA(msg->l2h[0])) {
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGDL(lctx.dl, LOGL_NOTICE, "EA bit 0 is not allowed in GSM\n");
|
2011-06-27 08:51:37 +00:00
|
|
|
msgb_free(msg);
|
2011-09-26 09:23:06 +00:00
|
|
|
rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, &mctx);
|
2011-06-27 08:51:37 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2011-09-26 09:23:06 +00:00
|
|
|
/* adress field */
|
|
|
|
lctx.lpd = LAPDm_ADDR_LPD(msg->l2h[0]);
|
|
|
|
lctx.sapi = LAPDm_ADDR_SAPI(msg->l2h[0]);
|
|
|
|
lctx.cr = LAPDm_ADDR_CR(msg->l2h[0]);
|
|
|
|
/* command field */
|
|
|
|
if (LAPDm_CTRL_is_I(msg->l2h[1])) {
|
|
|
|
lctx.format = LAPD_FORM_I;
|
|
|
|
lctx.n_send = LAPDm_CTRL_I_Ns(msg->l2h[1]);
|
|
|
|
lctx.n_recv = LAPDm_CTRL_Nr(msg->l2h[1]);
|
|
|
|
} else if (LAPDm_CTRL_is_S(msg->l2h[1])) {
|
|
|
|
lctx.format = LAPD_FORM_S;
|
|
|
|
lctx.n_recv = LAPDm_CTRL_Nr(msg->l2h[1]);
|
|
|
|
lctx.s_u = LAPDm_CTRL_S_BITS(msg->l2h[1]);
|
|
|
|
} else if (LAPDm_CTRL_is_U(msg->l2h[1])) {
|
|
|
|
lctx.format = LAPD_FORM_U;
|
|
|
|
lctx.s_u = LAPDm_CTRL_U_BITS(msg->l2h[1]);
|
|
|
|
} else
|
|
|
|
lctx.format = LAPD_FORM_UKN;
|
|
|
|
lctx.p_f = LAPDm_CTRL_PF_BIT(msg->l2h[1]);
|
|
|
|
if (lctx.sapi != LAPDm_SAPI_NORMAL
|
|
|
|
&& lctx.sapi != LAPDm_SAPI_SMS
|
|
|
|
&& lctx.format == LAPD_FORM_U
|
|
|
|
&& lctx.s_u == LAPDm_U_UI) {
|
|
|
|
/* 5.3.3 UI frames with invalid SAPI values shall be
|
|
|
|
* discarded
|
|
|
|
*/
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGDL(lctx.dl, LOGL_INFO, "sapi=%u (discarding)\n", lctx.sapi);
|
2011-09-26 09:23:06 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (mctx.lapdm_fmt == LAPDm_FMT_B4) {
|
|
|
|
lctx.n201 = n201;
|
|
|
|
lctx.length = n201;
|
|
|
|
lctx.more = 0;
|
|
|
|
msg->l3h = msg->l2h + 2;
|
2014-01-28 10:03:11 +00:00
|
|
|
msgb_pull_to_l3(msg);
|
2011-09-26 09:23:06 +00:00
|
|
|
} else {
|
|
|
|
/* length field */
|
|
|
|
if (!(msg->l2h[2] & LAPDm_EL)) {
|
|
|
|
/* G.4.1 If the EL bit is set to "0", an
|
|
|
|
* MDL-ERROR-INDICATION primitive with cause
|
|
|
|
* "frame not implemented" is sent to the
|
|
|
|
* mobile management entity. */
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGDL(lctx.dl, LOGL_NOTICE, "we don't support multi-octet length\n");
|
2011-09-26 09:23:06 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, &mctx);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
lctx.n201 = n201;
|
|
|
|
lctx.length = msg->l2h[2] >> 2;
|
|
|
|
lctx.more = !!(msg->l2h[2] & LAPDm_MORE);
|
|
|
|
msg->l3h = msg->l2h + 3;
|
2014-01-28 10:03:11 +00:00
|
|
|
msgb_pull_to_l3(msg);
|
2011-09-26 09:23:06 +00:00
|
|
|
}
|
|
|
|
/* store context for messages from lapd */
|
|
|
|
memcpy(&mctx.dl->mctx, &mctx, sizeof(mctx.dl->mctx));
|
2018-05-01 16:11:02 +00:00
|
|
|
rc =lapdm_rx_not_permitted(le, &lctx);
|
|
|
|
if (rc > 0) {
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGDL(lctx.dl, LOGL_NOTICE, "received message not permitted\n");
|
2018-05-01 16:11:02 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
rsl_rll_error(rc, &mctx);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2011-09-26 09:23:06 +00:00
|
|
|
/* send to LAPD */
|
|
|
|
rc = lapd_ph_data_ind(msg, &lctx);
|
2011-06-27 08:51:37 +00:00
|
|
|
break;
|
|
|
|
case LAPDm_FMT_Bter:
|
|
|
|
/* FIXME */
|
|
|
|
msgb_free(msg);
|
|
|
|
break;
|
|
|
|
case LAPDm_FMT_Bbis:
|
|
|
|
/* directly pass up to layer3 */
|
|
|
|
msg->l3h = msg->l2h;
|
2014-01-28 10:03:11 +00:00
|
|
|
msgb_pull_to_l3(msg);
|
2011-06-27 08:51:37 +00:00
|
|
|
rc = send_rslms_rll_l3(RSL_MT_UNIT_DATA_IND, &mctx, msg);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
msgb_free(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* input into layer2 (from layer 1) */
|
|
|
|
static int l2_ph_rach_ind(struct lapdm_entity *le, uint8_t ra, uint32_t fn, uint8_t acc_delay)
|
|
|
|
{
|
|
|
|
struct abis_rsl_cchan_hdr *ch;
|
|
|
|
struct gsm48_req_ref req_ref;
|
|
|
|
struct gsm_time gt;
|
|
|
|
struct msgb *msg = msgb_alloc_headroom(512, 64, "RSL CHAN RQD");
|
|
|
|
|
2015-04-09 12:22:21 +00:00
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2011-06-27 08:51:37 +00:00
|
|
|
msg->l2h = msgb_push(msg, sizeof(*ch));
|
|
|
|
ch = (struct abis_rsl_cchan_hdr *)msg->l2h;
|
|
|
|
rsl_init_cchan_hdr(ch, RSL_MT_CHAN_RQD);
|
|
|
|
ch->chan_nr = RSL_CHAN_RACH;
|
|
|
|
|
|
|
|
/* generate a RSL CHANNEL REQUIRED message */
|
|
|
|
gsm_fn2gsmtime(>, fn);
|
|
|
|
req_ref.ra = ra;
|
|
|
|
req_ref.t1 = gt.t1; /* FIXME: modulo? */
|
|
|
|
req_ref.t2 = gt.t2;
|
|
|
|
req_ref.t3_low = gt.t3 & 7;
|
|
|
|
req_ref.t3_high = gt.t3 >> 3;
|
|
|
|
|
|
|
|
msgb_tv_fixed_put(msg, RSL_IE_REQ_REFERENCE, 3, (uint8_t *) &req_ref);
|
|
|
|
msgb_tv_put(msg, RSL_IE_ACCESS_DELAY, acc_delay);
|
|
|
|
|
|
|
|
return rslms_sendmsg(msg, le);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int l2_ph_chan_conf(struct msgb *msg, struct lapdm_entity *le, uint32_t frame_nr);
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! Receive a PH-SAP primitive from L1 */
|
2011-06-27 08:51:37 +00:00
|
|
|
int lapdm_phsap_up(struct osmo_prim_hdr *oph, struct lapdm_entity *le)
|
|
|
|
{
|
|
|
|
struct osmo_phsap_prim *pp = (struct osmo_phsap_prim *) oph;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
if (oph->sap != SAP_GSM_PH) {
|
2011-09-26 09:23:06 +00:00
|
|
|
LOGP(DLLAPD, LOGL_ERROR, "primitive for unknown SAP %u\n",
|
2011-06-27 08:51:37 +00:00
|
|
|
oph->sap);
|
2013-02-05 11:01:32 +00:00
|
|
|
rc = -ENODEV;
|
|
|
|
goto free;
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (oph->primitive) {
|
|
|
|
case PRIM_PH_DATA:
|
|
|
|
if (oph->operation != PRIM_OP_INDICATION) {
|
2011-09-26 09:23:06 +00:00
|
|
|
LOGP(DLLAPD, LOGL_ERROR, "PH_DATA is not INDICATION %u\n",
|
2011-06-27 08:51:37 +00:00
|
|
|
oph->operation);
|
2013-02-05 11:01:32 +00:00
|
|
|
rc = -ENODEV;
|
|
|
|
goto free;
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
2022-10-09 16:49:19 +00:00
|
|
|
rc = l2_ph_data_ind(oph->msg, le, pp->u.data.fn, pp->u.data.chan_nr,
|
2011-06-27 08:51:37 +00:00
|
|
|
pp->u.data.link_id);
|
|
|
|
break;
|
|
|
|
case PRIM_PH_RTS:
|
|
|
|
if (oph->operation != PRIM_OP_INDICATION) {
|
2011-09-26 09:23:06 +00:00
|
|
|
LOGP(DLLAPD, LOGL_ERROR, "PH_RTS is not INDICATION %u\n",
|
2011-06-27 08:51:37 +00:00
|
|
|
oph->operation);
|
2013-02-05 11:01:32 +00:00
|
|
|
rc = -ENODEV;
|
|
|
|
goto free;
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
rc = l2_ph_data_conf(oph->msg, le);
|
|
|
|
break;
|
|
|
|
case PRIM_PH_RACH:
|
|
|
|
switch (oph->operation) {
|
|
|
|
case PRIM_OP_INDICATION:
|
|
|
|
rc = l2_ph_rach_ind(le, pp->u.rach_ind.ra, pp->u.rach_ind.fn,
|
|
|
|
pp->u.rach_ind.acc_delay);
|
|
|
|
break;
|
|
|
|
case PRIM_OP_CONFIRM:
|
|
|
|
rc = l2_ph_chan_conf(oph->msg, le, pp->u.rach_ind.fn);
|
|
|
|
break;
|
|
|
|
default:
|
2013-02-05 11:01:32 +00:00
|
|
|
rc = -EIO;
|
|
|
|
goto free;
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
break;
|
2013-02-05 11:01:32 +00:00
|
|
|
default:
|
|
|
|
LOGP(DLLAPD, LOGL_ERROR, "Unknown primitive %u\n",
|
|
|
|
oph->primitive);
|
|
|
|
rc = -EINVAL;
|
|
|
|
goto free;
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
2013-02-05 11:01:32 +00:00
|
|
|
|
|
|
|
free:
|
|
|
|
msgb_free(oph->msg);
|
|
|
|
return rc;
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* L3 -> L2 / RSLMS -> LAPDm */
|
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
/* Set LAPDm context for established connection */
|
|
|
|
static int set_lapdm_context(struct lapdm_datalink *dl, uint8_t chan_nr,
|
|
|
|
uint8_t link_id, int n201, uint8_t sapi)
|
|
|
|
{
|
|
|
|
memset(&dl->mctx, 0, sizeof(dl->mctx));
|
|
|
|
dl->mctx.dl = dl;
|
|
|
|
dl->mctx.chan_nr = chan_nr;
|
|
|
|
dl->mctx.link_id = link_id;
|
|
|
|
dl->dl.lctx.dl = &dl->dl;
|
|
|
|
dl->dl.lctx.n201 = n201;
|
|
|
|
dl->dl.lctx.sapi = sapi;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-06-27 08:51:37 +00:00
|
|
|
/* L3 requests establishment of data link */
|
|
|
|
static int rslms_rx_rll_est_req(struct msgb *msg, struct lapdm_datalink *dl)
|
|
|
|
{
|
|
|
|
struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
|
|
|
|
uint8_t chan_nr = rllh->chan_nr;
|
|
|
|
uint8_t link_id = rllh->link_id;
|
|
|
|
uint8_t sapi = rllh->link_id & 7;
|
|
|
|
struct tlv_parsed tv;
|
|
|
|
uint8_t length;
|
2011-11-06 19:43:08 +00:00
|
|
|
uint8_t n201 = (rllh->link_id & 0x40) ? N201_AB_SACCH : N201_AB_SDCCH;
|
2011-09-26 09:23:06 +00:00
|
|
|
struct osmo_dlsap_prim dp;
|
2011-06-27 08:51:37 +00:00
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
/* Set LAPDm context for established connection */
|
|
|
|
set_lapdm_context(dl, chan_nr, link_id, n201, sapi);
|
2011-06-27 08:51:37 +00:00
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg) - sizeof(*rllh));
|
2011-06-27 08:51:37 +00:00
|
|
|
if (TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
|
2011-09-26 09:23:06 +00:00
|
|
|
msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO);
|
2011-06-27 08:51:37 +00:00
|
|
|
/* contention resolution establishment procedure */
|
|
|
|
if (sapi != 0) {
|
|
|
|
/* According to clause 6, the contention resolution
|
|
|
|
* procedure is only permitted with SAPI value 0 */
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGDL(&dl->dl, LOGL_ERROR, "SAPI != 0 but contention"
|
2011-06-27 08:51:37 +00:00
|
|
|
"resolution (discarding)\n");
|
|
|
|
msgb_free(msg);
|
|
|
|
return send_rll_simple(RSL_MT_REL_IND, &dl->mctx);
|
|
|
|
}
|
|
|
|
/* transmit a SABM command with the P bit set to "1". The SABM
|
|
|
|
* command shall contain the layer 3 message unit */
|
|
|
|
length = TLVP_LEN(&tv, RSL_IE_L3_INFO);
|
|
|
|
} else {
|
|
|
|
/* normal establishment procedure */
|
2011-09-26 09:23:06 +00:00
|
|
|
msg->l3h = msg->l2h + sizeof(*rllh);
|
2011-06-27 08:51:37 +00:00
|
|
|
length = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if the layer3 message length exceeds N201 */
|
2011-11-06 19:43:08 +00:00
|
|
|
if (length > n201) {
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGDL(&dl->dl, LOGL_ERROR, "frame too large: %d > N201(%d) "
|
2011-11-06 19:43:08 +00:00
|
|
|
"(discarding)\n", length, n201);
|
2011-06-27 08:51:37 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return send_rll_simple(RSL_MT_REL_IND, &dl->mctx);
|
|
|
|
}
|
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
/* Remove RLL header from msgb and set length to L3-info */
|
2014-01-28 10:03:11 +00:00
|
|
|
msgb_pull_to_l3(msg);
|
|
|
|
msgb_trim(msg, length);
|
2011-06-27 08:51:37 +00:00
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
/* prepare prim */
|
|
|
|
osmo_prim_init(&dp.oph, 0, PRIM_DL_EST, PRIM_OP_REQUEST, msg);
|
|
|
|
|
|
|
|
/* send to L2 */
|
|
|
|
return lapd_recv_dlsap(&dp, &dl->dl.lctx);
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* L3 requests transfer of unnumbered information */
|
|
|
|
static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl)
|
|
|
|
{
|
2011-11-06 19:46:30 +00:00
|
|
|
struct lapdm_entity *le = dl->entity;
|
2011-06-27 08:51:37 +00:00
|
|
|
struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
|
|
|
|
uint8_t chan_nr = rllh->chan_nr;
|
|
|
|
uint8_t link_id = rllh->link_id;
|
|
|
|
uint8_t sapi = link_id & 7;
|
|
|
|
struct tlv_parsed tv;
|
2017-03-01 17:16:44 +00:00
|
|
|
int length, ui_bts;
|
|
|
|
|
|
|
|
if (!le) {
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGDL(&dl->dl, LOGL_ERROR, "lapdm_datalink without entity error\n");
|
2017-03-01 17:16:44 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return -EMLINK;
|
|
|
|
}
|
|
|
|
ui_bts = (le->mode == LAPDM_MODE_BTS && (link_id & 0x40));
|
2011-06-27 08:51:37 +00:00
|
|
|
|
|
|
|
/* check if the layer3 message length exceeds N201 */
|
|
|
|
|
|
|
|
rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
|
|
|
|
|
|
|
|
if (TLVP_PRESENT(&tv, RSL_IE_TIMING_ADVANCE)) {
|
2011-11-06 19:45:29 +00:00
|
|
|
le->ta = *TLVP_VAL(&tv, RSL_IE_TIMING_ADVANCE);
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
if (TLVP_PRESENT(&tv, RSL_IE_MS_POWER)) {
|
2011-11-06 19:45:29 +00:00
|
|
|
le->tx_power = *TLVP_VAL(&tv, RSL_IE_MS_POWER);
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGDL(&dl->dl, LOGL_ERROR, "unit data request without message error\n");
|
2011-06-27 08:51:37 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2011-09-26 09:23:06 +00:00
|
|
|
msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO);
|
2011-06-27 08:51:37 +00:00
|
|
|
length = TLVP_LEN(&tv, RSL_IE_L3_INFO);
|
|
|
|
/* check if the layer3 message length exceeds N201 */
|
2013-06-12 07:34:51 +00:00
|
|
|
if (length + ((link_id & 0x40) ? 4 : 2) + !ui_bts > 23) {
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGDL(&dl->dl, LOGL_ERROR, "frame too large: %d > N201(%d) "
|
2013-06-12 07:34:51 +00:00
|
|
|
"(discarding)\n", length,
|
|
|
|
((link_id & 0x40) ? 18 : 20) + ui_bts);
|
2011-06-27 08:51:37 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGDL(&dl->dl, LOGL_INFO, "sending unit data (tx_power=%d, ta=%d)\n", le->tx_power, le->ta);
|
2011-06-27 08:51:37 +00:00
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
/* Remove RLL header from msgb and set length to L3-info */
|
2014-01-28 10:03:11 +00:00
|
|
|
msgb_pull_to_l3(msg);
|
|
|
|
msgb_trim(msg, length);
|
2011-06-27 08:51:37 +00:00
|
|
|
|
|
|
|
/* Push L1 + LAPDm header on msgb */
|
2013-06-12 07:34:51 +00:00
|
|
|
msg->l2h = msgb_push(msg, 2 + !ui_bts);
|
|
|
|
msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, dl->dl.cr.loc2rem.cmd);
|
|
|
|
msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_UI, 0);
|
2011-11-06 19:46:30 +00:00
|
|
|
if (!ui_bts)
|
2013-06-12 07:34:51 +00:00
|
|
|
msg->l2h[2] = LAPDm_LEN(length);
|
|
|
|
if (link_id & 0x40) {
|
|
|
|
msg->l2h = msgb_push(msg, 2);
|
|
|
|
msg->l2h[0] = le->tx_power;
|
|
|
|
msg->l2h[1] = le->ta;
|
|
|
|
}
|
2011-06-27 08:51:37 +00:00
|
|
|
|
|
|
|
/* Tramsmit */
|
2011-09-26 09:23:06 +00:00
|
|
|
return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, 23);
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* L3 requests transfer of acknowledged information */
|
|
|
|
static int rslms_rx_rll_data_req(struct msgb *msg, struct lapdm_datalink *dl)
|
|
|
|
{
|
|
|
|
struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
|
|
|
|
struct tlv_parsed tv;
|
2011-09-26 09:23:06 +00:00
|
|
|
int length;
|
|
|
|
struct osmo_dlsap_prim dp;
|
2011-06-27 08:51:37 +00:00
|
|
|
|
|
|
|
rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
|
|
|
|
if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGDL(&dl->dl, LOGL_ERROR, "data request without message error\n");
|
2011-06-27 08:51:37 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2011-09-26 09:23:06 +00:00
|
|
|
msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO);
|
|
|
|
length = TLVP_LEN(&tv, RSL_IE_L3_INFO);
|
2011-06-27 08:51:37 +00:00
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
/* Remove RLL header from msgb and set length to L3-info */
|
2014-01-28 10:03:11 +00:00
|
|
|
msgb_pull_to_l3(msg);
|
|
|
|
msgb_trim(msg, length);
|
2011-06-27 08:51:37 +00:00
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
/* prepare prim */
|
|
|
|
osmo_prim_init(&dp.oph, 0, PRIM_DL_DATA, PRIM_OP_REQUEST, msg);
|
2011-06-27 08:51:37 +00:00
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
/* send to L2 */
|
|
|
|
return lapd_recv_dlsap(&dp, &dl->dl.lctx);
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* L3 requests suspension of data link */
|
|
|
|
static int rslms_rx_rll_susp_req(struct msgb *msg, struct lapdm_datalink *dl)
|
|
|
|
{
|
|
|
|
struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
|
|
|
|
uint8_t sapi = rllh->link_id & 7;
|
2011-09-26 09:23:06 +00:00
|
|
|
struct osmo_dlsap_prim dp;
|
2011-06-27 08:51:37 +00:00
|
|
|
|
|
|
|
if (sapi != 0) {
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGDL(&dl->dl, LOGL_ERROR, "SAPI != 0 while suspending\n");
|
2011-06-27 08:51:37 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
/* prepare prim */
|
|
|
|
osmo_prim_init(&dp.oph, 0, PRIM_DL_SUSP, PRIM_OP_REQUEST, msg);
|
2011-06-27 08:51:37 +00:00
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
/* send to L2 */
|
|
|
|
return lapd_recv_dlsap(&dp, &dl->dl.lctx);
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* L3 requests resume of data link */
|
|
|
|
static int rslms_rx_rll_res_req(struct msgb *msg, struct lapdm_datalink *dl)
|
|
|
|
{
|
|
|
|
struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
|
2011-09-26 09:23:06 +00:00
|
|
|
int msg_type = rllh->c.msg_type;
|
2011-06-27 08:51:37 +00:00
|
|
|
uint8_t chan_nr = rllh->chan_nr;
|
|
|
|
uint8_t link_id = rllh->link_id;
|
|
|
|
uint8_t sapi = rllh->link_id & 7;
|
|
|
|
struct tlv_parsed tv;
|
|
|
|
uint8_t length;
|
2011-11-06 19:43:08 +00:00
|
|
|
uint8_t n201 = (rllh->link_id & 0x40) ? N201_AB_SACCH : N201_AB_SDCCH;
|
2011-09-26 09:23:06 +00:00
|
|
|
struct osmo_dlsap_prim dp;
|
2011-06-27 08:51:37 +00:00
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
/* Set LAPDm context for established connection */
|
|
|
|
set_lapdm_context(dl, chan_nr, link_id, n201, sapi);
|
2011-06-27 08:51:37 +00:00
|
|
|
|
|
|
|
rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
|
|
|
|
if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGDL(&dl->dl, LOGL_ERROR, "resume without message error\n");
|
2011-06-27 08:51:37 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return send_rll_simple(RSL_MT_REL_IND, &dl->mctx);
|
|
|
|
}
|
2011-09-26 09:23:06 +00:00
|
|
|
msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO);
|
2011-06-27 08:51:37 +00:00
|
|
|
length = TLVP_LEN(&tv, RSL_IE_L3_INFO);
|
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
/* Remove RLL header from msgb and set length to L3-info */
|
2014-01-28 10:03:11 +00:00
|
|
|
msgb_pull_to_l3(msg);
|
|
|
|
msgb_trim(msg, length);
|
2011-06-27 08:51:37 +00:00
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
/* prepare prim */
|
|
|
|
osmo_prim_init(&dp.oph, 0, (msg_type == RSL_MT_RES_REQ) ? PRIM_DL_RES
|
|
|
|
: PRIM_DL_RECON, PRIM_OP_REQUEST, msg);
|
2011-06-27 08:51:37 +00:00
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
/* send to L2 */
|
|
|
|
return lapd_recv_dlsap(&dp, &dl->dl.lctx);
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* L3 requests release of data link */
|
|
|
|
static int rslms_rx_rll_rel_req(struct msgb *msg, struct lapdm_datalink *dl)
|
|
|
|
{
|
|
|
|
struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
|
|
|
|
uint8_t mode = 0;
|
2011-09-26 09:23:06 +00:00
|
|
|
struct osmo_dlsap_prim dp;
|
2011-06-27 08:51:37 +00:00
|
|
|
|
|
|
|
/* get release mode */
|
|
|
|
if (rllh->data[0] == RSL_IE_RELEASE_MODE)
|
|
|
|
mode = rllh->data[1] & 1;
|
|
|
|
|
|
|
|
/* Pull rllh */
|
2014-01-28 10:03:11 +00:00
|
|
|
msgb_pull_to_l3(msg);
|
2012-04-26 19:50:54 +00:00
|
|
|
|
|
|
|
/* 04.06 3.8.3: No information field is permitted with the DISC
|
|
|
|
* command. */
|
2014-01-28 10:03:11 +00:00
|
|
|
msgb_trim(msg, 0);
|
2011-06-27 08:51:37 +00:00
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
/* prepare prim */
|
|
|
|
osmo_prim_init(&dp.oph, 0, PRIM_DL_REL, PRIM_OP_REQUEST, msg);
|
|
|
|
dp.u.rel_req.mode = mode;
|
2011-06-27 08:51:37 +00:00
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
/* send to L2 */
|
|
|
|
return lapd_recv_dlsap(&dp, &dl->dl.lctx);
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* L3 requests channel in idle state */
|
|
|
|
static int rslms_rx_chan_rqd(struct lapdm_channel *lc, struct msgb *msg)
|
|
|
|
{
|
|
|
|
struct abis_rsl_cchan_hdr *cch = msgb_l2(msg);
|
|
|
|
void *l1ctx = lc->lapdm_dcch.l1_ctx;
|
|
|
|
struct osmo_phsap_prim pp;
|
|
|
|
|
|
|
|
osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_RACH,
|
|
|
|
PRIM_OP_REQUEST, NULL);
|
|
|
|
|
|
|
|
if (msgb_l2len(msg) < sizeof(*cch) + 4 + 2 + 2) {
|
2011-09-26 09:23:06 +00:00
|
|
|
LOGP(DLLAPD, LOGL_ERROR, "Message too short for CHAN RQD!\n");
|
2011-06-27 08:51:37 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (cch->data[0] != RSL_IE_REQ_REFERENCE) {
|
2011-09-26 09:23:06 +00:00
|
|
|
LOGP(DLLAPD, LOGL_ERROR, "Missing REQ REFERENCE IE\n");
|
2011-06-27 08:51:37 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
pp.u.rach_req.ra = cch->data[1];
|
|
|
|
pp.u.rach_req.offset = ((cch->data[2] & 0x7f) << 8) | cch->data[3];
|
|
|
|
pp.u.rach_req.is_combined_ccch = cch->data[2] >> 7;
|
|
|
|
|
|
|
|
if (cch->data[4] != RSL_IE_ACCESS_DELAY) {
|
2011-09-26 09:23:06 +00:00
|
|
|
LOGP(DLLAPD, LOGL_ERROR, "Missing ACCESS_DELAY IE\n");
|
2011-06-27 08:51:37 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
/* TA = 0 - delay */
|
|
|
|
pp.u.rach_req.ta = 0 - cch->data[5];
|
|
|
|
|
|
|
|
if (cch->data[6] != RSL_IE_MS_POWER) {
|
2011-09-26 09:23:06 +00:00
|
|
|
LOGP(DLLAPD, LOGL_ERROR, "Missing MS POWER IE\n");
|
2011-06-27 08:51:37 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
pp.u.rach_req.tx_power = cch->data[7];
|
|
|
|
|
|
|
|
msgb_free(msg);
|
|
|
|
|
|
|
|
return lc->lapdm_dcch.l1_prim_cb(&pp.oph, l1ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* L1 confirms channel request */
|
|
|
|
static int l2_ph_chan_conf(struct msgb *msg, struct lapdm_entity *le, uint32_t frame_nr)
|
|
|
|
{
|
|
|
|
struct abis_rsl_cchan_hdr *ch;
|
|
|
|
struct gsm_time tm;
|
|
|
|
struct gsm48_req_ref *ref;
|
|
|
|
|
|
|
|
gsm_fn2gsmtime(&tm, frame_nr);
|
|
|
|
|
2014-01-28 10:03:11 +00:00
|
|
|
msgb_pull_to_l3(msg);
|
2011-06-27 08:51:37 +00:00
|
|
|
msg->l2h = msgb_push(msg, sizeof(*ch) + sizeof(*ref));
|
|
|
|
ch = (struct abis_rsl_cchan_hdr *)msg->l2h;
|
|
|
|
rsl_init_cchan_hdr(ch, RSL_MT_CHAN_CONF);
|
|
|
|
ch->chan_nr = RSL_CHAN_RACH;
|
|
|
|
ch->data[0] = RSL_IE_REQ_REFERENCE;
|
|
|
|
ref = (struct gsm48_req_ref *) (ch->data + 1);
|
|
|
|
ref->t1 = tm.t1;
|
|
|
|
ref->t2 = tm.t2;
|
|
|
|
ref->t3_low = tm.t3 & 0x7;
|
|
|
|
ref->t3_high = tm.t3 >> 3;
|
2017-12-08 13:30:47 +00:00
|
|
|
|
2011-06-27 08:51:37 +00:00
|
|
|
return rslms_sendmsg(msg, le);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* incoming RSLms RLL message from L3 */
|
|
|
|
static int rslms_rx_rll(struct msgb *msg, struct lapdm_channel *lc)
|
|
|
|
{
|
|
|
|
struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
|
|
|
|
int msg_type = rllh->c.msg_type;
|
|
|
|
uint8_t sapi = rllh->link_id & 7;
|
|
|
|
struct lapdm_entity *le;
|
|
|
|
struct lapdm_datalink *dl;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
if (msgb_l2len(msg) < sizeof(*rllh)) {
|
2011-09-26 09:23:06 +00:00
|
|
|
LOGP(DLLAPD, LOGL_ERROR, "Message too short for RLL hdr!\n");
|
2011-11-06 19:31:47 +00:00
|
|
|
msgb_free(msg);
|
2011-06-27 08:51:37 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rllh->link_id & 0x40)
|
|
|
|
le = &lc->lapdm_acch;
|
|
|
|
else
|
|
|
|
le = &lc->lapdm_dcch;
|
|
|
|
|
2015-12-14 14:26:07 +00:00
|
|
|
/* 4.1.1.5 / 4.1.1.6 / 4.1.1.7 all only exist on MS side, not
|
|
|
|
* BTS side */
|
|
|
|
if (le->mode == LAPDM_MODE_BTS) {
|
|
|
|
switch (msg_type) {
|
|
|
|
case RSL_MT_SUSP_REQ:
|
|
|
|
case RSL_MT_RES_REQ:
|
|
|
|
case RSL_MT_RECON_REQ:
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGP(DLLAPD, LOGL_NOTICE, "(%s) RLL Message '%s' unsupported in BTS side LAPDm\n",
|
2015-12-14 14:26:07 +00:00
|
|
|
lc->name, rsl_msg_name(msg_type));
|
|
|
|
msgb_free(msg);
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-23 14:00:55 +00:00
|
|
|
/* G.2.1 No action shall be taken on frames containing an unallocated
|
2011-06-27 08:51:37 +00:00
|
|
|
* SAPI.
|
|
|
|
*/
|
2014-03-26 12:45:17 +00:00
|
|
|
dl = lapdm_datalink_for_sapi(le, sapi);
|
2011-06-27 08:51:37 +00:00
|
|
|
if (!dl) {
|
2011-09-26 09:23:06 +00:00
|
|
|
LOGP(DLLAPD, LOGL_ERROR, "No instance for SAPI %d!\n", sapi);
|
2011-11-06 19:31:47 +00:00
|
|
|
msgb_free(msg);
|
2011-06-27 08:51:37 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2012-12-25 22:15:50 +00:00
|
|
|
switch (msg_type) {
|
|
|
|
case RSL_MT_DATA_REQ:
|
|
|
|
case RSL_MT_SUSP_REQ:
|
|
|
|
case RSL_MT_REL_REQ:
|
|
|
|
/* This is triggered in abnormal error conditions where
|
|
|
|
* set_lapdm_context() was not called for the channel earlier. */
|
|
|
|
if (!dl->dl.lctx.dl) {
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGP(DLLAPD, LOGL_NOTICE, "(%s) RLL Message '%s' received without LAPDm context. (sapi %d)\n",
|
2012-12-25 22:15:50 +00:00
|
|
|
lc->name, rsl_msg_name(msg_type), sapi);
|
|
|
|
msgb_free(msg);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2020-05-02 17:56:36 +00:00
|
|
|
LOGP(DLLAPD, LOGL_INFO, "(%s) RLL Message '%s' received. (sapi %d)\n",
|
2012-12-25 22:15:50 +00:00
|
|
|
lc->name, rsl_msg_name(msg_type), sapi);
|
|
|
|
}
|
2011-06-27 08:51:37 +00:00
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
switch (msg_type) {
|
|
|
|
case RSL_MT_UNIT_DATA_REQ:
|
|
|
|
rc = rslms_rx_rll_udata_req(msg, dl);
|
|
|
|
break;
|
|
|
|
case RSL_MT_EST_REQ:
|
|
|
|
rc = rslms_rx_rll_est_req(msg, dl);
|
|
|
|
break;
|
|
|
|
case RSL_MT_DATA_REQ:
|
|
|
|
rc = rslms_rx_rll_data_req(msg, dl);
|
|
|
|
break;
|
|
|
|
case RSL_MT_SUSP_REQ:
|
|
|
|
rc = rslms_rx_rll_susp_req(msg, dl);
|
|
|
|
break;
|
|
|
|
case RSL_MT_RES_REQ:
|
|
|
|
rc = rslms_rx_rll_res_req(msg, dl);
|
|
|
|
break;
|
|
|
|
case RSL_MT_RECON_REQ:
|
|
|
|
rc = rslms_rx_rll_res_req(msg, dl);
|
|
|
|
break;
|
|
|
|
case RSL_MT_REL_REQ:
|
|
|
|
rc = rslms_rx_rll_rel_req(msg, dl);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOGP(DLLAPD, LOGL_NOTICE, "Message unsupported.\n");
|
2011-06-27 08:51:37 +00:00
|
|
|
msgb_free(msg);
|
2011-09-26 09:23:06 +00:00
|
|
|
rc = -EINVAL;
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* incoming RSLms COMMON CHANNEL message from L3 */
|
|
|
|
static int rslms_rx_com_chan(struct msgb *msg, struct lapdm_channel *lc)
|
|
|
|
{
|
|
|
|
struct abis_rsl_cchan_hdr *cch = msgb_l2(msg);
|
|
|
|
int msg_type = cch->c.msg_type;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
if (msgb_l2len(msg) < sizeof(*cch)) {
|
2011-09-26 09:23:06 +00:00
|
|
|
LOGP(DLLAPD, LOGL_ERROR, "Message too short for COM CHAN hdr!\n");
|
2011-06-27 08:51:37 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (msg_type) {
|
|
|
|
case RSL_MT_CHAN_RQD:
|
|
|
|
/* create and send RACH request */
|
|
|
|
rc = rslms_rx_chan_rqd(lc, msg);
|
|
|
|
break;
|
|
|
|
default:
|
2011-09-26 09:23:06 +00:00
|
|
|
LOGP(DLLAPD, LOGL_NOTICE, "Unknown COMMON CHANNEL msg %d!\n",
|
2011-06-27 08:51:37 +00:00
|
|
|
msg_type);
|
|
|
|
msgb_free(msg);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-05-19 10:17:06 +00:00
|
|
|
/*! Receive a RSLms \ref msgb from Layer 3. 'msg' ownership is transferred,
|
|
|
|
* i.e. caller must not free it */
|
2011-06-27 08:51:37 +00:00
|
|
|
int lapdm_rslms_recvmsg(struct msgb *msg, struct lapdm_channel *lc)
|
|
|
|
{
|
|
|
|
struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
if (msgb_l2len(msg) < sizeof(*rslh)) {
|
2011-09-26 09:23:06 +00:00
|
|
|
LOGP(DLLAPD, LOGL_ERROR, "Message too short RSL hdr!\n");
|
2019-05-19 10:17:06 +00:00
|
|
|
msgb_free(msg);
|
2011-06-27 08:51:37 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (rslh->msg_discr & 0xfe) {
|
|
|
|
case ABIS_RSL_MDISC_RLL:
|
|
|
|
rc = rslms_rx_rll(msg, lc);
|
|
|
|
break;
|
|
|
|
case ABIS_RSL_MDISC_COM_CHAN:
|
|
|
|
rc = rslms_rx_com_chan(msg, lc);
|
|
|
|
break;
|
|
|
|
default:
|
2011-09-26 09:23:06 +00:00
|
|
|
LOGP(DLLAPD, LOGL_ERROR, "unknown RSLms message "
|
2011-06-27 08:51:37 +00:00
|
|
|
"discriminator 0x%02x", rslh->msg_discr);
|
|
|
|
msgb_free(msg);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! Set the \ref lapdm_mode of a LAPDm entity */
|
2011-06-27 08:51:37 +00:00
|
|
|
int lapdm_entity_set_mode(struct lapdm_entity *le, enum lapdm_mode mode)
|
|
|
|
{
|
2011-09-26 09:23:06 +00:00
|
|
|
int i;
|
|
|
|
enum lapd_mode lm;
|
|
|
|
|
2011-06-27 08:51:37 +00:00
|
|
|
switch (mode) {
|
|
|
|
case LAPDM_MODE_MS:
|
2011-09-26 09:23:06 +00:00
|
|
|
lm = LAPD_MODE_USER;
|
2011-06-27 08:51:37 +00:00
|
|
|
break;
|
|
|
|
case LAPDM_MODE_BTS:
|
2011-09-26 09:23:06 +00:00
|
|
|
lm = LAPD_MODE_NETWORK;
|
2011-06-27 08:51:37 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-09-26 09:23:06 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(le->datalink); i++) {
|
|
|
|
lapd_set_mode(&le->datalink[i].dl, lm);
|
|
|
|
}
|
|
|
|
|
2011-06-27 08:51:37 +00:00
|
|
|
le->mode = mode;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! Set the \ref lapdm_mode of a LAPDm channel*/
|
2011-06-27 08:51:37 +00:00
|
|
|
int lapdm_channel_set_mode(struct lapdm_channel *lc, enum lapdm_mode mode)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = lapdm_entity_set_mode(&lc->lapdm_dcch, mode);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
return lapdm_entity_set_mode(&lc->lapdm_acch, mode);
|
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! Set the L1 callback and context of a LAPDm channel */
|
2011-06-27 08:51:37 +00:00
|
|
|
void lapdm_channel_set_l1(struct lapdm_channel *lc, osmo_prim_cb cb, void *ctx)
|
|
|
|
{
|
|
|
|
lc->lapdm_dcch.l1_prim_cb = cb;
|
|
|
|
lc->lapdm_acch.l1_prim_cb = cb;
|
|
|
|
lc->lapdm_dcch.l1_ctx = ctx;
|
|
|
|
lc->lapdm_acch.l1_ctx = ctx;
|
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! Set the L3 callback and context of a LAPDm channel */
|
2011-06-27 08:51:37 +00:00
|
|
|
void lapdm_channel_set_l3(struct lapdm_channel *lc, lapdm_cb_t cb, void *ctx)
|
|
|
|
{
|
|
|
|
lc->lapdm_dcch.l3_cb = cb;
|
|
|
|
lc->lapdm_acch.l3_cb = cb;
|
|
|
|
lc->lapdm_dcch.l3_ctx = ctx;
|
|
|
|
lc->lapdm_acch.l3_ctx = ctx;
|
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! Reset an entire LAPDm entity and all its datalinks */
|
2011-06-27 08:51:37 +00:00
|
|
|
void lapdm_entity_reset(struct lapdm_entity *le)
|
|
|
|
{
|
|
|
|
struct lapdm_datalink *dl;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(le->datalink); i++) {
|
|
|
|
dl = &le->datalink[i];
|
2011-09-26 09:23:06 +00:00
|
|
|
lapd_dl_reset(&dl->dl);
|
2011-06-27 08:51:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! Reset a LAPDm channel with all its entities */
|
2011-06-27 08:51:37 +00:00
|
|
|
void lapdm_channel_reset(struct lapdm_channel *lc)
|
|
|
|
{
|
|
|
|
lapdm_entity_reset(&lc->lapdm_dcch);
|
|
|
|
lapdm_entity_reset(&lc->lapdm_acch);
|
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! Set the flags of a LAPDm entity */
|
2011-06-27 08:51:37 +00:00
|
|
|
void lapdm_entity_set_flags(struct lapdm_entity *le, unsigned int flags)
|
|
|
|
{
|
|
|
|
le->flags = flags;
|
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! Set the flags of all LAPDm entities in a LAPDm channel */
|
2011-06-27 08:51:37 +00:00
|
|
|
void lapdm_channel_set_flags(struct lapdm_channel *lc, unsigned int flags)
|
|
|
|
{
|
|
|
|
lapdm_entity_set_flags(&lc->lapdm_dcch, flags);
|
|
|
|
lapdm_entity_set_flags(&lc->lapdm_acch, flags);
|
|
|
|
}
|
2011-08-17 16:22:08 +00:00
|
|
|
|
2012-04-18 19:53:23 +00:00
|
|
|
/*! @} */
|