aboutsummaryrefslogtreecommitdiffstats
path: root/src/gsm
diff options
context:
space:
mode:
authorHarald Welte <laforge@osmocom.org>2022-11-29 22:33:54 +0100
committerHarald Welte <laforge@gnumonks.org>2023-01-21 22:41:57 +0100
commitd55a209d1c1eaf48d11d851c6a5082af7df36d69 (patch)
treef6395ad8e875a619354e9687951af086b5e3cee1 /src/gsm
parent88955fb5506280a82860b026f8d7db9ad57bb504 (diff)
create libosmoisdn sub-library
There are some parts of libosmogsm which are not really GSM specific, but rather ISDN bits that were inherited by GSM. This includes the I.460 multiplex as well as the core LAPD protocol. Let's move those bits to its own libosmoisdn library, before we add more ISDN specific bits to the wrong place. Backwards-compatibility is created by making libosmogsm depend on libosmoisdn, and by providing wrapper include files for source compatibility. Change-Id: Ib1a6c762322fd5047be3188b1df22408ef06aa50
Diffstat (limited to 'src/gsm')
-rw-r--r--src/gsm/Makefile.am6
-rw-r--r--src/gsm/i460_mux.c388
-rw-r--r--src/gsm/lapd_core.c2237
3 files changed, 3 insertions, 2628 deletions
diff --git a/src/gsm/Makefile.am b/src/gsm/Makefile.am
index eb0f7e45..aa2e6ec2 100644
--- a/src/gsm/Makefile.am
+++ b/src/gsm/Makefile.am
@@ -25,18 +25,18 @@ libgsmint_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c comp128v23.c \
gsm48_ie.c gsm0808.c sysinfo.c \
gprs_cipher_core.c gprs_rlc.c gsm0480.c abis_nm.c gsm0502.c \
gsm0411_utils.c gsm0411_smc.c gsm0411_smr.c gsm0414.c \
- lapd_core.c lapdm.c kasumi.c gsm29205.c gsm_04_08_gprs.c \
+ lapdm.c kasumi.c gsm29205.c gsm_04_08_gprs.c \
auth_core.c auth_comp128v1.c auth_comp128v23.c auth_xor.c \
auth_milenage.c milenage/aes-encblock.c gea.c \
milenage/aes-internal.c milenage/aes-internal-enc.c \
milenage/milenage.c gan.c ipa.c gsm0341.c apn.c \
gsup.c gsup_sms.c gprs_gea.c gsm0503_conv.c oap.c gsm0808_utils.c \
gsm23003.c gsm23236.c mncc.c bts_features.c oap_client.c \
- gsm29118.c gsm48_rest_octets.c cbsp.c gsm48049.c i460_mux.c \
+ gsm29118.c gsm48_rest_octets.c cbsp.c gsm48049.c \
gad.c bsslap.c bssmap_le.c kdf.c iuup.c
libgsmint_la_LDFLAGS = -no-undefined
-libgsmint_la_LIBADD = $(top_builddir)/src/core/libosmocore.la
+libgsmint_la_LIBADD = $(top_builddir)/src/core/libosmocore.la $(top_builddir)/src/isdn/libosmoisdn.la
libosmogsm_la_SOURCES =
libosmogsm_la_LDFLAGS = $(LTLDFLAGS_OSMOGSM) -version-info $(LIBVERSION) -no-undefined
diff --git a/src/gsm/i460_mux.c b/src/gsm/i460_mux.c
deleted file mode 100644
index a6a0835c..00000000
--- a/src/gsm/i460_mux.c
+++ /dev/null
@@ -1,388 +0,0 @@
-/*! \file i460_mux.c
- * ITU-T I.460 sub-channel multiplexer + demultiplexer */
-/*
- * (C) 2020 by Harald Welte <laforge@gnumonks.org>
- *
- * SPDX-License-Identifier: GPL-2.0+
- *
- * 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.
- */
-
-#include <errno.h>
-
-#include <osmocom/core/bits.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/gsm/i460_mux.h>
-
-/* count the number of sub-channels in this I460 slot */
-static int osmo_i460_subchan_count(struct osmo_i460_timeslot *ts)
-{
- int i, num_used = 0;
-
- for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
- if (ts->schan[i].rate != OSMO_I460_RATE_NONE)
- num_used++;
- }
-
- return num_used;
-}
-
-/* does this channel have no sub-streams (single 64k subchannel)? */
-static bool osmo_i460_has_single_64k_schan(struct osmo_i460_timeslot *ts)
-{
- if (osmo_i460_subchan_count(ts) != 1)
- return false;
-
- if (ts->schan[0].rate != OSMO_I460_RATE_64k)
- return false;
-
- return true;
-}
-
-/***********************************************************************
- * Demultiplexer
- ***********************************************************************/
-
-/* append a single bit to a sub-channel */
-static void demux_subchan_append_bit(struct osmo_i460_subchan *schan, uint8_t bit)
-{
- struct osmo_i460_subchan_demux *demux = &schan->demux;
-
- OSMO_ASSERT(demux->out_bitbuf);
- OSMO_ASSERT(demux->out_idx < demux->out_bitbuf_size);
-
- demux->out_bitbuf[demux->out_idx++] = bit ? 1 : 0;
-
- if (demux->out_idx >= demux->out_bitbuf_size) {
- if (demux->out_cb_bits)
- demux->out_cb_bits(schan, demux->user_data, demux->out_bitbuf, demux->out_idx);
- else {
- /* pack bits into bytes */
- OSMO_ASSERT((demux->out_idx % 8) == 0);
- unsigned int num_bytes = demux->out_idx / 8;
- uint8_t bytes[num_bytes];
- osmo_ubit2pbit(bytes, demux->out_bitbuf, demux->out_idx);
- demux->out_cb_bytes(schan, demux->user_data, bytes, num_bytes);
- }
- demux->out_idx = 0;
- }
-}
-
-/* extract those bits relevant to this schan of each byte in 'data' */
-static void demux_subchan_extract_bits(struct osmo_i460_subchan *schan, const uint8_t *data, size_t data_len)
-{
- int i;
-
- for (i = 0; i < data_len; i++) {
- uint8_t inbyte = data[i];
- /* I.460 defines sub-channel 0 is using bit positions 1+2 (the two
- * most significant bits, hence we extract msb-first */
- uint8_t inbits = inbyte << schan->bit_offset;
-
- /* extract the bits relevant to the given schan */
- switch (schan->rate) {
- case OSMO_I460_RATE_8k:
- demux_subchan_append_bit(schan, inbits & 0x80);
- break;
- case OSMO_I460_RATE_16k:
- demux_subchan_append_bit(schan, inbits & 0x80);
- demux_subchan_append_bit(schan, inbits & 0x40);
- break;
- case OSMO_I460_RATE_32k:
- demux_subchan_append_bit(schan, inbits & 0x80);
- demux_subchan_append_bit(schan, inbits & 0x40);
- demux_subchan_append_bit(schan, inbits & 0x20);
- demux_subchan_append_bit(schan, inbits & 0x10);
- break;
- case OSMO_I460_RATE_64k:
- demux_subchan_append_bit(schan, inbits & 0x80);
- demux_subchan_append_bit(schan, inbits & 0x40);
- demux_subchan_append_bit(schan, inbits & 0x20);
- demux_subchan_append_bit(schan, inbits & 0x10);
- demux_subchan_append_bit(schan, inbits & 0x08);
- demux_subchan_append_bit(schan, inbits & 0x04);
- demux_subchan_append_bit(schan, inbits & 0x02);
- demux_subchan_append_bit(schan, inbits & 0x01);
- break;
- default:
- OSMO_ASSERT(0);
- }
- }
-}
-
-/*! Data from E1 timeslot into de-multiplexer
- * \param[in] ts timeslot state
- * \param[in] data input data bytes as received from E1/T1
- * \param[in] data_len length of data in bytes */
-void osmo_i460_demux_in(struct osmo_i460_timeslot *ts, const uint8_t *data, size_t data_len)
-{
- struct osmo_i460_subchan *schan;
- struct osmo_i460_subchan_demux *demux;
- int i;
-
- /* fast path if entire 64k slot is used */
- if (osmo_i460_has_single_64k_schan(ts)) {
- schan = &ts->schan[0];
- demux = &schan->demux;
- if (demux->out_cb_bytes)
- demux->out_cb_bytes(schan, demux->user_data, data, data_len);
- else {
- ubit_t bits[data_len*8];
- osmo_pbit2ubit(bits, data, data_len*8);
- demux->out_cb_bits(schan, demux->user_data, bits, data_len*8);
- }
- return;
- }
-
- /* Slow path iterating over all lchans */
- for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
- schan = &ts->schan[i];
- if (schan->rate == OSMO_I460_RATE_NONE)
- continue;
- demux_subchan_extract_bits(schan, data, data_len);
- }
-}
-
-
-/***********************************************************************
- * Multiplexer
- ***********************************************************************/
-
-/*! enqueue a to-be-transmitted message buffer containing unpacked bits */
-void osmo_i460_mux_enqueue(struct osmo_i460_subchan *schan, struct msgb *msg)
-{
- OSMO_ASSERT(msgb_length(msg) > 0);
- msgb_enqueue(&schan->mux.tx_queue, msg);
-}
-
-/* mux: pull the next bit out of the given sub-channel */
-static ubit_t mux_schan_provide_bit(struct osmo_i460_subchan *schan)
-{
- struct osmo_i460_subchan_mux *mux = &schan->mux;
- struct msgb *msg;
- ubit_t bit;
-
- /* if we don't have anything to transmit, return '1' bits */
- if (llist_empty(&mux->tx_queue)) {
- /* User code now has a last chance to put something into the queue. */
- if (mux->in_cb_queue_empty)
- mux->in_cb_queue_empty(schan, mux->user_data);
-
- /* If the queue is still empty, return idle bits */
- if (llist_empty(&mux->tx_queue))
- return 0x01;
- }
- msg = llist_entry(mux->tx_queue.next, struct msgb, list);
- bit = msgb_pull_u8(msg);
-
- /* free msgb if we have pulled the last bit */
- if (msgb_length(msg) <= 0) {
- llist_del(&msg->list);
- talloc_free(msg);
- }
-
- return bit;
-}
-
-/*! provide one byte with the subchan-specific bits of given sub-channel.
- * \param[in] schan sub-channel that is to provide bits
- * \parma[out] mask bitmask of those bits filled in
- * \returns bits of given sub-channel */
-static uint8_t mux_subchan_provide_bits(struct osmo_i460_subchan *schan, uint8_t *mask)
-{
- uint8_t outbits = 0;
- uint8_t outmask;
-
- /* I.460 defines sub-channel 0 is using bit positions 1+2 (the two
- * most significant bits, hence we provide msb-first */
-
- switch (schan->rate) {
- case OSMO_I460_RATE_8k:
- outbits = mux_schan_provide_bit(schan) << 7;
- outmask = 0x80;
- break;
- case OSMO_I460_RATE_16k:
- outbits |= mux_schan_provide_bit(schan) << 7;
- outbits |= mux_schan_provide_bit(schan) << 6;
- outmask = 0xC0;
- break;
- case OSMO_I460_RATE_32k:
- outbits |= mux_schan_provide_bit(schan) << 7;
- outbits |= mux_schan_provide_bit(schan) << 6;
- outbits |= mux_schan_provide_bit(schan) << 5;
- outbits |= mux_schan_provide_bit(schan) << 4;
- outmask = 0xF0;
- break;
- case OSMO_I460_RATE_64k:
- outbits |= mux_schan_provide_bit(schan) << 7;
- outbits |= mux_schan_provide_bit(schan) << 6;
- outbits |= mux_schan_provide_bit(schan) << 5;
- outbits |= mux_schan_provide_bit(schan) << 4;
- outbits |= mux_schan_provide_bit(schan) << 3;
- outbits |= mux_schan_provide_bit(schan) << 2;
- outbits |= mux_schan_provide_bit(schan) << 1;
- outbits |= mux_schan_provide_bit(schan) << 0;
- outmask = 0xFF;
- break;
- default:
- OSMO_ASSERT(0);
- }
- *mask = outmask >> schan->bit_offset;
- return outbits >> schan->bit_offset;
-}
-
-/* provide one byte of multiplexed I.460 bits */
-static uint8_t mux_timeslot_provide_bits(struct osmo_i460_timeslot *ts)
-{
- int i, count = 0;
- uint8_t ret = 0xff; /* unused bits must be '1' as per I.460 */
-
- for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
- struct osmo_i460_subchan *schan = &ts->schan[i];
- uint8_t bits, mask;
-
- if (schan->rate == OSMO_I460_RATE_NONE)
- continue;
- count++;
- bits = mux_subchan_provide_bits(schan, &mask);
- ret &= ~mask;
- ret |= bits;
- }
-
- return ret;
-}
-
-
-/*! Data from E1 timeslot into de-multiplexer
- * \param[in] ts timeslot state
- * \param[out] out caller-provided buffer where to store generated output bytes
- * \param[in] out_len number of bytes to be stored at out
- */
-int osmo_i460_mux_out(struct osmo_i460_timeslot *ts, uint8_t *out, size_t out_len)
-{
- int i;
-
- /* fast path if entire 64k slot is used */
- //if (osmo_i460_has_single_64k_schan(ts)) { }
-
- for (i = 0; i < out_len; i++)
- out[i] = mux_timeslot_provide_bits(ts);
-
- return out_len;
-}
-
-
-/***********************************************************************
- * Initialization / Control
- ***********************************************************************/
-
-
-static int alloc_bitbuf(void *ctx, struct osmo_i460_subchan *schan, size_t num_bits)
-{
- struct osmo_i460_subchan_demux *demux = &schan->demux;
-
- talloc_free(demux->out_bitbuf);
- demux->out_bitbuf = talloc_zero_size(ctx, num_bits);
- if (!demux->out_bitbuf)
- return -ENOMEM;
- demux->out_bitbuf_size = num_bits;
-
- return 0;
-}
-
-
-static int find_unused_subchan_idx(const struct osmo_i460_timeslot *ts)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
- const struct osmo_i460_subchan *schan = &ts->schan[i];
- if (schan->rate == OSMO_I460_RATE_NONE)
- return i;
- }
- return -1;
-}
-
-/* reset subchannel struct into a defined state */
-static void subchan_reset(struct osmo_i460_subchan *schan, bool first_time)
-{
- /* Before we zero out the subchannel struct, we must be sure that the
- * tx_queue is cleared and all dynamically allocated memory is freed.
- * However, on an uninitalized subchannel struct we can not be sure
- * that the pointers are valid. If the subchannel is reset the first
- * time the caller must set first_time to true. */
- if (!first_time) {
- if (schan->demux.out_bitbuf)
- talloc_free(schan->demux.out_bitbuf);
- msgb_queue_free(&schan->mux.tx_queue);
- }
-
- /* Reset subchannel to a defined state */
- memset(schan, 0, sizeof(*schan));
- schan->rate = OSMO_I460_RATE_NONE;
- INIT_LLIST_HEAD(&schan->mux.tx_queue);
-}
-
-/*! initialize an I.460 timeslot */
-void osmo_i460_ts_init(struct osmo_i460_timeslot *ts)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
- struct osmo_i460_subchan *schan = &ts->schan[i];
- schan->ts = ts;
- subchan_reset(schan, true);
- }
-}
-
-/*! add a new sub-channel to the given timeslot
- * \param[in] ctx talloc context from where to allocate the internal buffer
- * \param[in] ts timeslot to which to add a sub-channel
- * \param[in] chd description of the sub-channel to be added
- * \return pointer to sub-channel on success, NULL on error */
-struct osmo_i460_subchan *
-osmo_i460_subchan_add(void *ctx, struct osmo_i460_timeslot *ts, const struct osmo_i460_schan_desc *chd)
-{
- struct osmo_i460_subchan *schan;
- int idx, rc;
-
- idx = find_unused_subchan_idx(ts);
- if (idx < 0)
- return NULL;
-
- schan = &ts->schan[idx];
-
- schan->rate = chd->rate;
- schan->bit_offset = chd->bit_offset;
-
- schan->demux.out_cb_bits = chd->demux.out_cb_bits;
- schan->demux.out_cb_bytes = chd->demux.out_cb_bytes;
- schan->demux.user_data = chd->demux.user_data;
- schan->mux.in_cb_queue_empty = chd->mux.in_cb_queue_empty;
- schan->mux.user_data = chd->mux.user_data;
- rc = alloc_bitbuf(ctx, schan, chd->demux.num_bits);
- if (rc < 0) {
- subchan_reset(schan, false);
- return NULL;
- }
-
- /* return number of schan in use */
- return schan;
-}
-
-/* remove a su-channel from the multiplex */
-void osmo_i460_subchan_del(struct osmo_i460_subchan *schan)
-{
- subchan_reset(schan, false);
-}
-
-/*! @} */
diff --git a/src/gsm/lapd_core.c b/src/gsm/lapd_core.c
deleted file mode 100644
index 7441285d..00000000
--- a/src/gsm/lapd_core.c
+++ /dev/null
@@ -1,2237 +0,0 @@
-/*! \file lapd_core.c
- * LAPD core implementation */
-/*
- * (C) 2010-2020 by Harald Welte <laforge@gnumonks.org>
- * (C) 2010-2011 by Andreas Eversberg <jolly@eversberg.eu>
- *
- * All Rights Reserved
- *
- * SPDX-License-Identifier: GPL-2.0+
- *
- * 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.
- *
- */
-
-/*! \addtogroup lapd
- * @{
- *
- * Osmocom LAPD core, used for Q.921, LAPDm and others.
- *
- * Notes on Buffering: rcv_buffer, tx_queue, tx_hist, send_buffer, send_queue
- *
- * RX data is stored in the rcv_buffer (pointer). If the message is complete, it
- * is removed from rcv_buffer pointer and forwarded to L3. If the RX data is
- * received while there is an incomplete rcv_buffer, it is appended to it.
- *
- * TX data is stored in the send_queue first. When transmitting a frame,
- * the first message in the send_queue is moved to the send_buffer. There it
- * resides until all fragments are acknowledged. Fragments to be sent by I
- * frames are stored in the tx_hist buffer for resend, if required. Also the
- * current fragment is copied into the tx_queue. There it resides until it is
- * forwarded to layer 1.
- *
- * In case we have SAPI 0, we only have a window size of 1, so the unack-
- * nowledged message resides always in the send_buffer. In case of a suspend,
- * it can be written back to the first position of the send_queue.
- *
- * The layer 1 normally sends a PH-READY-TO-SEND. But because we use
- * asynchronous transfer between layer 1 and layer 2 (serial link), we must
- * send a frame before layer 1 reaches the right timeslot to send it. So we
- * move the tx_queue to layer 1 when there is not already a pending frame, and
- * wait until acknowledge after the frame has been sent. If we receive an
- * acknowledge, we can send the next frame from the buffer, if any.
- *
- * The moving of tx_queue to layer 1 may also trigger T200, if desired. Also it
- * will trigger next I frame, if possible.
- *
- * T203 is optional. It will be stated when entering MF EST state. It will also
- * be started when I or S frame is received in that state . It will be
- * restarted in the lapd_acknowledge() function, in case outstanding frames
- * will not trigger T200. It will be stoped, when T200 is started in MF EST
- * state. It will also be stoped when leaving MF EST state.
- *
- * \file lapd_core.c
- */
-
-/* Enable this to test content resolution on network side:
- * - The first SABM is received, UA is dropped.
- * - The phone repeats SABM, but it's content is wrong, so it is ignored
- * - The phone repeats SABM again, content is right, so UA is sent.
- */
-//#define TEST_CONTENT_RESOLUTION_NETWORK
-
-#include <stdio.h>
-#include <stdbool.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/core/talloc.h>
-#include <osmocom/gsm/lapd_core.h>
-#include <osmocom/gsm/rsl.h>
-
-/* TS 04.06 Table 4 / Section 3.8.1 */
-#define LAPD_U_SABM 0x7
-#define LAPD_U_SABME 0xf
-#define LAPD_U_DM 0x3
-#define LAPD_U_UI 0x0
-#define LAPD_U_DISC 0x8
-#define LAPD_U_UA 0xC
-#define LAPD_U_FRMR 0x11
-
-#define LAPD_S_RR 0x0
-#define LAPD_S_RNR 0x1
-#define LAPD_S_REJ 0x2
-
-#define CR_USER2NET_CMD 0
-#define CR_USER2NET_RESP 1
-#define CR_NET2USER_CMD 1
-#define CR_NET2USER_RESP 0
-
-#define LAPD_HEADROOM 56
-#define LAPD_TAILROOM 16
-
-#define SBIT(a) (1 << a)
-#define ALL_STATES 0xffffffff
-
-static void lapd_t200_cb(void *data);
-static void lapd_t203_cb(void *data);
-static int lapd_send_i(struct lapd_msg_ctx *lctx, int line);
-static int lapd_est_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx);
-
-/* UTILITY FUNCTIONS */
-
-struct msgb *lapd_msgb_alloc(int length, const char *name)
-{
- /* adding space for padding, FIXME: add as an option */
- if (length < 21)
- length = 21;
- return msgb_alloc_headroom(length + LAPD_HEADROOM + LAPD_TAILROOM, LAPD_HEADROOM, name);
-}
-
-static inline uint8_t do_mod(uint8_t x, uint8_t m)
-{
- return x & (m - 1);
-}
-
-static inline uint8_t inc_mod(uint8_t x, uint8_t m)
-{
- return (x + 1) & (m - 1);
-}
-
-static inline uint8_t add_mod(uint8_t x, uint8_t y, uint8_t m)
-{
- return (x + y) & (m - 1);
-}
-
-static inline uint8_t sub_mod(uint8_t x, uint8_t y, uint8_t m)
-{
- return (x - y) & (m - 1); /* handle negative results correctly */
-}
-
-static void lapd_dl_flush_send(struct lapd_datalink *dl)
-{
- struct msgb *msg;
-
- /* Flush send-queue */
- while ((msg = msgb_dequeue(&dl->send_queue)))
- msgb_free(msg);
-
- /* Clear send-buffer */
- msgb_free(dl->send_buffer);
- dl->send_buffer = NULL;
-}
-
-static void lapd_dl_flush_hist(struct lapd_datalink *dl)
-{
- unsigned int i;
-
- if (!dl->range_hist || !dl->tx_hist)
- return;
-
- for (i = 0; i < dl->range_hist; i++) {
- if (dl->tx_hist[i].msg) {
- msgb_free(dl->tx_hist[i].msg);
- dl->tx_hist[i].msg = NULL;
- }
- }
-}
-
-static void lapd_dl_flush_tx(struct lapd_datalink *dl)
-{
- struct msgb *msg;
-
- while ((msg = msgb_dequeue(&dl->tx_queue)))
- msgb_free(msg);
- lapd_dl_flush_hist(dl);
-}
-
-/* Figure B.2/Q.921 */
-const struct value_string lapd_state_names[] = {
- OSMO_VALUE_STRING(LAPD_STATE_NULL),
- OSMO_VALUE_STRING(LAPD_STATE_TEI_UNASS),
- OSMO_VALUE_STRING(LAPD_STATE_ASS_TEI_WAIT),
- OSMO_VALUE_STRING(LAPD_STATE_EST_TEI_WAIT),
- OSMO_VALUE_STRING(LAPD_STATE_IDLE),
- OSMO_VALUE_STRING(LAPD_STATE_SABM_SENT),
- OSMO_VALUE_STRING(LAPD_STATE_DISC_SENT),
- OSMO_VALUE_STRING(LAPD_STATE_MF_EST),
- OSMO_VALUE_STRING(LAPD_STATE_TIMER_RECOV),
- { 0, NULL }
-};
-
-static inline const char *lapd_state_name(enum lapd_state state)
-{
- return get_value_string(lapd_state_names, 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);
-}
-
-static void lapd_start_t203(struct lapd_datalink *dl)
-{
- if (osmo_timer_pending(&dl->t203))
- return;
- LOGDL(dl, LOGL_INFO, "start T203\n");
- osmo_timer_schedule(&dl->t203, dl->t203_sec, dl->t203_usec);
-}
-
-static void lapd_stop_t200(struct lapd_datalink *dl)
-{
- if (!osmo_timer_pending(&dl->t200))
- return;
- LOGDL(dl, LOGL_INFO, "stop T200\n");
- osmo_timer_del(&dl->t200);
-}
-
-static void lapd_stop_t203(struct lapd_datalink *dl)
-{
- if (!osmo_timer_pending(&dl->t203))
- return;
- LOGDL(dl, LOGL_INFO, "stop T203\n");
- osmo_timer_del(&dl->t203);
-}
-
-static void lapd_dl_newstate(struct lapd_datalink *dl, uint32_t state)
-{
- LOGDL(dl, LOGL_INFO, "new state %s -> %s\n",
- lapd_state_name(dl->state), lapd_state_name(state));
-
- if (state != LAPD_STATE_MF_EST && dl->state == LAPD_STATE_MF_EST) {
- /* stop T203 on leaving MF EST state, if running */
- lapd_stop_t203(dl);
- /* remove content res. (network side) on leaving MF EST state */
- msgb_free(dl->cont_res);
- dl->cont_res = NULL;
- }
-
- /* start T203 on entering MF EST state, if enabled */
- if ((dl->t203_sec || dl->t203_usec)
- && state == LAPD_STATE_MF_EST && dl->state != LAPD_STATE_MF_EST)
- lapd_start_t203(dl);
-
- dl->state = state;
-}
-
-void *tall_lapd_ctx = NULL;
-
-/*! Initialize LAPD datalink instance and allocate history
- * \param[in] dl caller-allocated datalink structure
- * \param[in] k maximum number of unacknowledged frames
- * \param[in] v_range range of sequence numbers
- * \param[in] maxf maximum frame size (after defragmentation)
- * \param[in] name human-readable name for this LAPD datalink */
-void lapd_dl_init2(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf,
- const char *name)
-{
- int m;
-
- memset(dl, 0, sizeof(*dl));
- INIT_LLIST_HEAD(&dl->send_queue);
- INIT_LLIST_HEAD(&dl->tx_queue);
- dl->reestablish = 1;
- dl->n200_est_rel = 3;
- dl->n200 = 3;
- dl->t200_sec = 1;
- dl->t200_usec = 0;
- osmo_timer_setup(&dl->t200, lapd_t200_cb, dl);
- dl->t203_sec = 10;
- dl->t203_usec = 0;
- osmo_timer_setup(&dl->t203, lapd_t203_cb, dl);
- dl->maxf = maxf;
- if (k > v_range - 1)
- k = v_range - 1;
- dl->k = k;
- dl->v_range = v_range;
-
- /* Calculate modulo for history array:
- * - The history range must be at least k+1.
- * - The history range must be 2^x, where x is as low as possible.
- */
- k++;
- for (m = 0x80; m; m >>= 1) {
- if ((m & k)) {
- if (k > m)
- m <<= 1;
- dl->range_hist = m;
- break;
- }
- }
-
- if (!tall_lapd_ctx) {
- tall_lapd_ctx = talloc_named_const(NULL, 1, "lapd context");
- OSMO_ASSERT(tall_lapd_ctx);
- }
-
- talloc_free(dl->name);
- if (name)
- dl->name = talloc_strdup(tall_lapd_ctx, name);
- else
- dl->name = talloc_asprintf(tall_lapd_ctx, "dl=%p", dl);
-
- LOGDL(dl, LOGL_INFO, "Init DL layer: sequence range = %d, k = %d, "
- "history range = %d\n", dl->v_range, dl->k, dl->range_hist);
-
- lapd_dl_newstate(dl, LAPD_STATE_IDLE);
-
- dl->tx_hist = talloc_zero_array(tall_lapd_ctx,
- struct lapd_history, dl->range_hist);
-}
-
-/*! Initialize LAPD datalink instance and allocate history
- * \param[in] dl caller-allocated datalink structure
- * \param[in] k maximum number of unacknowledged frames
- * \param[in] v_range range of sequence numbers
- * \param[in] maxf maximum frame size (after defragmentation) */
-void lapd_dl_init(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf)
-{
- lapd_dl_init2(dl, k, v_range, maxf, NULL);
-}
-
-void lapd_dl_set_name(struct lapd_datalink *dl, const char *name)
-{
- if (!name)
- return;
- osmo_talloc_replace_string(tall_lapd_ctx, &dl->name, name);
-}
-
-/* reset to IDLE state */
-void lapd_dl_reset(struct lapd_datalink *dl)
-{
- LOGDL(dl, LOGL_INFO, "Resetting LAPD instance\n");
- /* enter idle state (and remove eventual cont_res) */
- lapd_dl_newstate(dl, LAPD_STATE_IDLE);
- /* flush buffer */
- lapd_dl_flush_tx(dl);
- lapd_dl_flush_send(dl);
- /* Discard partly received L3 message */
- msgb_free(dl->rcv_buffer);
- dl->rcv_buffer = NULL;
- /* stop Timers */
- lapd_stop_t200(dl);
- lapd_stop_t203(dl);
- if (dl->state == LAPD_STATE_IDLE)
- return;
- /* enter idle state (and remove eventual cont_res) */
- lapd_dl_newstate(dl, LAPD_STATE_IDLE);
-}
-
-/* reset and de-allocate history buffer */
-void lapd_dl_exit(struct lapd_datalink *dl)
-{
- /* free all ressources except history buffer */
- lapd_dl_reset(dl);
-
- /* enter null state */
- lapd_dl_newstate(dl, LAPD_STATE_NULL);
-
- /* free history buffer list */
- talloc_free(dl->tx_hist);
- dl->tx_hist = NULL;
- talloc_free(dl->name);
- dl->name = NULL;
-}
-
-/*! Set the \ref lapdm_mode of a LAPDm entity */
-int lapd_set_mode(struct lapd_datalink *dl, enum lapd_mode mode)
-{
- switch (mode) {
- case LAPD_MODE_USER:
- dl->cr.loc2rem.cmd = CR_USER2NET_CMD;
- dl->cr.loc2rem.resp = CR_USER2NET_RESP;
- dl->cr.rem2loc.cmd = CR_NET2USER_CMD;
- dl->cr.rem2loc.resp = CR_NET2USER_RESP;
- break;
- case LAPD_MODE_NETWORK:
- dl->cr.loc2rem.cmd = CR_NET2USER_CMD;
- dl->cr.loc2rem.resp = CR_NET2USER_RESP;
- dl->cr.rem2loc.cmd = CR_USER2NET_CMD;
- dl->cr.rem2loc.resp = CR_USER2NET_RESP;
- break;
- default:
- return -EINVAL;
- }
- dl->mode = mode;
-
- return 0;
-}
-
-/* send DL message with optional msgb */
-static int send_dl_l3(uint8_t prim, uint8_t op, struct lapd_msg_ctx *lctx,
- struct msgb *msg)
-{
- struct lapd_datalink *dl = lctx->dl;
- struct osmo_dlsap_prim dp;
-
- osmo_prim_init(&dp.oph, 0, prim, op, msg);
- return dl->send_dlsap(&dp, lctx);
-}
-
-/* send simple DL message */
-static inline int send_dl_simple(uint8_t prim, uint8_t op,
- struct lapd_msg_ctx *lctx)
-{
- return send_dl_l3(prim, op, lctx, NULL);
-}
-
-/* send MDL-ERROR INDICATION */
-static int mdl_error(uint8_t cause, struct lapd_msg_ctx *lctx)
-{
- struct lapd_datalink *dl = lctx->dl;
- struct osmo_dlsap_prim dp;
-
- LOGDL(dl, LOGL_NOTICE,
- "sending MDL-ERROR-IND cause %d from state %s\n",
- cause, lapd_state_name(dl->state));
- osmo_prim_init(&dp.oph, 0, PRIM_MDL_ERROR, PRIM_OP_INDICATION, NULL);
- dp.u.error_ind.cause = cause;
- return dl->send_dlsap(&dp, lctx);
-}
-
-/* send UA response */
-static int lapd_send_ua(struct lapd_msg_ctx *lctx, uint8_t len, uint8_t *data)
-{
- struct msgb *msg = lapd_msgb_alloc(len, "LAPD UA");
- struct lapd_msg_ctx nctx;
- struct lapd_datalink *dl = lctx->dl;
-
- memcpy(&nctx, lctx, sizeof(nctx));
- msg->l3h = msgb_put(msg, len);
- if (len)
- memcpy(msg->l3h, data, len);
- /* keep nctx.ldp */
- /* keep nctx.sapi */
- /* keep nctx.tei */
- nctx.cr = dl->cr.loc2rem.resp;
- nctx.format = LAPD_FORM_U;
- nctx.s_u = LAPD_U_UA;
- /* keep nctx.p_f */
- nctx.length = len;
- nctx.more = 0;
-
- return dl->send_ph_data_req(&nctx, msg);
-}
-
-/* send DM response */
-static int lapd_send_dm(struct lapd_msg_ctx *lctx)
-{
- struct msgb *msg = lapd_msgb_alloc(0, "LAPD DM");
- struct lapd_msg_ctx nctx;
- struct lapd_datalink *dl = lctx->dl;
-
- memcpy(&nctx, lctx, sizeof(nctx));
- /* keep nctx.ldp */
- /* keep nctx.sapi */
- /* keep nctx.tei */
- nctx.cr = dl->cr.loc2rem.resp;
- nctx.format = LAPD_FORM_U;
- nctx.s_u = LAPD_U_DM;
- /* keep nctx.p_f */
- nctx.length = 0;
- nctx.more = 0;
-
- return dl->send_ph_data_req(&nctx, msg);
-}
-
-/* send RR response / command */
-static int lapd_send_rr(struct lapd_msg_ctx *lctx, uint8_t f_bit, uint8_t cmd)
-{
- struct msgb *msg = lapd_msgb_alloc(0, "LAPD RR");
- struct lapd_msg_ctx nctx;
- struct lapd_datalink *dl = lctx->dl;
-
- memcpy(&nctx, lctx, sizeof(nctx));
- /* keep nctx.ldp */
- /* keep nctx.sapi */
- /* keep nctx.tei */
- nctx.cr = (cmd) ? dl->cr.loc2rem.cmd : dl->cr.loc2rem.resp;
- nctx.format = LAPD_FORM_S;
- nctx.s_u = LAPD_S_RR;
- nctx.p_f = f_bit;
- nctx.n_recv = dl->v_recv;
- nctx.length = 0;
- nctx.more = 0;
-
- return dl->send_ph_data_req(&nctx, msg);
-}
-
-/* send RNR response / command */
-static int lapd_send_rnr(struct lapd_msg_ctx *lctx, uint8_t f_bit, uint8_t cmd)
-{
- struct msgb *msg = lapd_msgb_alloc(0, "LAPD RNR");
- struct lapd_msg_ctx nctx;
- struct lapd_datalink *dl = lctx->dl;
-
- memcpy(&nctx, lctx, sizeof(nctx));
- /* keep nctx.ldp */
- /* keep nctx.sapi */
- /* keep nctx.tei */
- nctx.cr = (cmd) ? dl->cr.loc2rem.cmd : dl->cr.loc2rem.resp;
- nctx.format = LAPD_FORM_S;
- nctx.s_u = LAPD_S_RNR;
- nctx.p_f = f_bit;
- nctx.n_recv = dl->v_recv;
- nctx.length = 0;
- nctx.more = 0;
-
- return dl->send_ph_data_req(&nctx, msg);
-}
-
-/* send REJ response */
-static int lapd_send_rej(struct lapd_msg_ctx *lctx, uint8_t f_bit)
-{
- struct msgb *msg = lapd_msgb_alloc(0, "LAPD REJ");
- struct lapd_msg_ctx nctx;
- struct lapd_datalink *dl = lctx->dl;
-
- memcpy(&nctx, lctx, sizeof(nctx));
- /* keep nctx.ldp */
- /* keep nctx.sapi */
- /* keep nctx.tei */
- nctx.cr = dl->cr.loc2rem.resp;
- nctx.format = LAPD_FORM_S;
- nctx.s_u = LAPD_S_REJ;
- nctx.p_f = f_bit;
- nctx.n_recv = dl->v_recv;
- nctx.length = 0;
- nctx.more = 0;
-
- return dl->send_ph_data_req(&nctx, msg);
-}
-
-/* resend SABM or DISC message */
-static int lapd_send_resend(struct lapd_datalink *dl)
-{
- struct msgb *msg;
- uint8_t h = do_mod(dl->v_send, dl->range_hist);
- int length = dl->tx_hist[h].msg->len;
- struct lapd_msg_ctx nctx;
-
- /* assemble message */
- memcpy(&nctx, &dl->lctx, sizeof(nctx));
- /* keep nctx.ldp */
- /* keep nctx.sapi */
- /* keep nctx.tei */
- nctx.cr = dl->cr.loc2rem.cmd;
- nctx.format = LAPD_FORM_U;
- if (dl->state == LAPD_STATE_SABM_SENT)
- nctx.s_u = (dl->use_sabme) ? LAPD_U_SABME : LAPD_U_SABM;
- else
- nctx.s_u = LAPD_U_DISC;
- nctx.p_f = 1;
- nctx.length = length;
- nctx.more = 0;
-
- /* Resend SABM/DISC from tx_hist */
- msg = lapd_msgb_alloc(length, "LAPD resend");
- msg->l3h = msgb_put(msg, length);
- if (length)
- memcpy(msg->l3h, dl->tx_hist[h].msg->data, length);
-
- return dl->send_ph_data_req(&nctx, msg);
-}
-
-/* reestablish link */
-static int lapd_reestablish(struct lapd_datalink *dl)
-{
- struct osmo_dlsap_prim dp;
- struct msgb *msg;
-
- LOGDL(dl, LOGL_DEBUG, "LAPD reestablish\n");
-
- msg = lapd_msgb_alloc(0, "DUMMY");
- osmo_prim_init(&dp.oph, 0, PRIM_DL_EST, PRIM_OP_REQUEST, msg);
-
- return lapd_est_req(&dp, &dl->lctx);
-}
-
-/* Timer callback on T200 expiry */
-static void lapd_t200_cb(void *data)
-{
- struct lapd_datalink *dl = data;
-
- LOGDL(dl, LOGL_INFO, "Timeout T200 state=%s\n", lapd_state_name(dl->state));
-
- switch (dl->state) {
- case LAPD_STATE_SABM_SENT:
- /* 5.4.1.3 */
- if (dl->retrans_ctr >= dl->n200_est_rel + 1) {
- /* flush tx and send buffers */
- lapd_dl_flush_tx(dl);
- lapd_dl_flush_send(dl);
- /* go back to idle state */
- lapd_dl_newstate(dl, LAPD_STATE_IDLE);
- /* NOTE: we must not change any other states or buffers
- * and queues, since we may reconnect after handover
- * failure. the buffered messages is replaced there */
- /* send MDL ERROR INIDCATION to L3 */
- mdl_error(MDL_CAUSE_T200_EXPIRED, &dl->lctx);
- /* send RELEASE INDICATION to L3 */
- send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION,
- &dl->lctx);
- break;
- }
- /* retransmit SABM command */
- lapd_send_resend(dl);
- /* increment re-transmission counter */
- dl->retrans_ctr++;
- /* restart T200 (PH-READY-TO-SEND) */
- lapd_start_t200(dl);
- break;
- case LAPD_STATE_DISC_SENT:
- /* 5.4.4.3 */
- if (dl->retrans_ctr >= dl->n200_est_rel + 1) {
- /* send MDL ERROR INIDCATION to L3 */
- mdl_error(MDL_CAUSE_T200_EXPIRED, &dl->lctx);
- /* flush tx and send buffers */
- lapd_dl_flush_tx(dl);
- lapd_dl_flush_send(dl);
- /* go back to idle state */
- lapd_dl_newstate(dl, LAPD_STATE_IDLE);
- /* NOTE: we must not change any other states or buffers
- * and queues, since we may reconnect after handover
- * failure. the buffered messages is replaced there */
- /* send RELEASE INDICATION to L3 */
- send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, &dl->lctx);
- break;
- }
- /* retransmit DISC command */
- lapd_send_resend(dl);
- /* increment re-transmission counter */
- dl->retrans_ctr++;
- /* restart T200 (PH-READY-TO-SEND) */
- lapd_start_t200(dl);
- break;
- case LAPD_STATE_MF_EST:
- /* 5.5.7 */
- dl->retrans_ctr = 0;
- lapd_dl_newstate(dl, LAPD_STATE_TIMER_RECOV);
- /* fall through */
- case LAPD_STATE_TIMER_RECOV:
- dl->retrans_ctr++;
- if (dl->retrans_ctr <= dl->n200) {
- uint8_t vs = sub_mod(dl->v_send, 1, dl->v_range);
- uint8_t h = do_mod(vs, dl->range_hist);
- /* retransmit I frame (V_s-1) with P=1, if any */
- if (dl->tx_hist[h].msg) {
- struct msgb *msg;
- int length = dl->tx_hist[h].msg->len;
- struct lapd_msg_ctx nctx;
-
- LOGDL(dl, LOGL_INFO, "retransmit last frame V(S)=%d\n", vs);
- /* Create I frame (segment) from tx_hist */
- memcpy(&nctx, &dl->lctx, sizeof(nctx));
- /* keep nctx.ldp */
- /* keep nctx.sapi */
- /* keep nctx.tei */
- nctx.cr = dl->cr.loc2rem.cmd;
- nctx.format = LAPD_FORM_I;
- nctx.p_f = 1;
- nctx.n_send = vs;
- nctx.n_recv = dl->v_recv;
- nctx.length = length;
- nctx.more = dl->tx_hist[h].more;
- msg = lapd_msgb_alloc(length, "LAPD I resend");
- msg->l3h = msgb_put(msg, length);
- memcpy(msg->l3h, dl->tx_hist[h].msg->data,
- length);
- dl->send_ph_data_req(&nctx, msg);
- } else {
- /* OR send appropriate supervision frame with P=1 */
- if (!dl->own_busy && !dl->seq_err_cond) {
- lapd_send_rr(&dl->lctx, 1, 1);
- /* NOTE: In case of sequence error
- * condition, the REJ frame has been
- * transmitted when entering the
- * condition, so it has not be done
- * here
- */
- } else if (dl->own_busy) {
- lapd_send_rnr(&dl->lctx, 1, 1);
- } else {
- LOGDL(dl, LOGL_INFO, "unhandled, pls. fix\n");
- }
- }
- /* restart T200 (PH-READY-TO-SEND) */
- lapd_start_t200(dl);
- } else {
- /* send MDL ERROR INIDCATION to L3 */
- mdl_error(MDL_CAUSE_T200_EXPIRED, &dl->lctx);
- /* reestablish */
- if (!dl->reestablish)
- break;
- LOGDL(dl, LOGL_NOTICE, "N200+1 reached, performingreestablishment\n");
- lapd_reestablish(dl);
- }
- break;
- default:
- LOGDL(dl, LOGL_INFO, "T200 expired in unexpected dl->state %s)\n",
- lapd_state_name(dl->state));
- }
-}
-
-/* Timer callback on T203 expiry */
-static void lapd_t203_cb(void *data)
-{
- struct lapd_datalink *dl = data;
-
- LOGDL(dl, LOGL_INFO, "Timeout T203 state=%s\n", lapd_state_name(dl->state));
-
- if (dl->state != LAPD_STATE_MF_EST) {
- LOGDL(dl, LOGL_ERROR, "T203 fired outside MF EST state, please fix!\n");
- return;
- }
-
- /* set retransmission counter to 0 */
- dl->retrans_ctr = 0;
- /* enter timer recovery state */
- lapd_dl_newstate(dl, LAPD_STATE_TIMER_RECOV);
- /* transmit a supervisory command with P bit set to 1 as follows: */
- if (!dl->own_busy) {
- LOGDL(dl, LOGL_INFO, "transmit an RR poll command\n");
- /* Send RR with P=1 */
- lapd_send_rr(&dl->lctx, 1, 1);
- } else {
- LOGDL(dl, LOGL_INFO, "transmit an RNR poll command\n");
- /* Send RNR with P=1 */
- lapd_send_rnr(&dl->lctx, 1, 1);
- }
- /* start T200 */
- lapd_start_t200(dl);
-}
-
-/* 5.5.3.1: Common function to acknowlege frames up to the given N(R) value */
-static void lapd_acknowledge(struct lapd_msg_ctx *lctx)
-{
- struct lapd_datalink *dl = lctx->dl;
- uint8_t nr = lctx->n_recv;
- int s = 0, rej = 0;
- bool t200_reset = false;
- int i, h;
-
- /* supervisory frame ? */
- if (lctx->format == LAPD_FORM_S)
- s = 1;
- /* REJ frame ? */
- if (s && lctx->s_u == LAPD_S_REJ)
- rej = 1;
-
- /* Flush all transmit buffers of acknowledged frames */
- for (i = dl->v_ack; i != nr; i = inc_mod(i, dl->v_range)) {
- h = do_mod(i, dl->range_hist);
- if (dl->tx_hist[h].msg) {
- msgb_free(dl->tx_hist[h].msg);
- dl->tx_hist[h].msg = NULL;
- LOGDL(dl, LOGL_INFO, "ack frame %d\n", i);
- }
- }
-
- if (dl->state != LAPD_STATE_TIMER_RECOV) {
- /* When not in the timer recovery condition, the data
- * link layer entity shall reset the timer T200 on
- * receipt of a valid I frame with N(R) higher than V(A),
- * or an REJ with an N(R) equal to V(A). */
- if ((!rej && nr != dl->v_ack) || (rej && nr == dl->v_ack)) {
- t200_reset = true;
- lapd_stop_t200(dl);
- /* 5.5.3.1 Note 1 + 2 imply timer recovery cond. */
- }
- /* 5.7.4: N(R) sequence error
- * N(R) is called valid, if and only if
- * (N(R)-V(A)) mod 8 <= (V(S)-V(A)) mod 8.
- */
- if (sub_mod(nr, dl->v_ack, dl->v_range) > sub_mod(dl->v_send, dl->v_ack, dl->v_range)) {
- LOGDL(dl, LOGL_NOTICE, "N(R) sequence error\n");
- mdl_error(MDL_CAUSE_SEQ_ERR, lctx);
- }
- }
-
- /* V(A) shall be set to the value of N(R) */
- dl->v_ack = nr;
-
- /* If T200 has been stopped by the receipt of an I, RR or RNR frame,
- * and if there are outstanding I frames, restart T200 */
- if (t200_reset && !rej) {
- if (dl->tx_hist[sub_mod(dl->v_send, 1, dl->range_hist)].msg) {
- LOGDL(dl, LOGL_INFO, "start T200, due to unacked I frame(s)\n");
- lapd_start_t200(dl);
- }
- }
-
- /* This also does a restart, when I or S frame is received */
-
- /* 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)) {
- lapd_start_t203(dl);
- }
-}
-
-/* L1 -> L2 */
-
-/* Receive a LAPD U SABM(E) message from L1 */
-static int lapd_rx_u_sabm(struct msgb *msg, struct lapd_msg_ctx *lctx)
-{
- struct lapd_datalink *dl = lctx->dl;
- int length = lctx->length;
- int rc = 0;
- uint8_t prim, op;
-
- prim = PRIM_DL_EST;
- op = PRIM_OP_INDICATION;
-
- LOGDL(dl, LOGL_INFO, "SABM(E) received in state %s\n", lapd_state_name(dl->state));
- /* 5.7.1 */
- dl->seq_err_cond = 0;
- /* G.2.2 Wrong value of the C/R bit */
- if (lctx->cr == dl->cr.rem2loc.resp) {
- LOGDL(dl, LOGL_ERROR, "SABM response error\n");
- msgb_free(msg);
- mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
- return -EINVAL;
- }
-
- /* G.4.5 If SABM is received with L>N201 or with M bit
- * set, AN MDL-ERROR-INDICATION is sent to MM.
- */
- if (lctx->more || length > lctx->n201) {
- LOGDL(dl, LOGL_ERROR, "SABM too large error\n");
- msgb_free(msg);
- mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
- return -EIO;
- }
-
- switch (dl->state) {
- case LAPD_STATE_IDLE:
- break;
- case LAPD_STATE_TIMER_RECOV:
- LOGDL(dl, LOGL_INFO, "SABM command, timer recovery state\n");
- /* If link is lost on the remote side, we start over
- * and send DL-ESTABLISH indication again. */
- /* 3GPP TS 44.006 8.6.3 "Procedures for re-establishment" */
- if (length) {
- /* check for contention resoultion */
- LOGDL(dl, LOGL_ERROR, "SABM L>0 not expected in timer "
- "recovery state\n");
- mdl_error(MDL_CAUSE_SABM_INFO_NOTALL, lctx);
- lapd_send_dm(lctx);
- msgb_free(msg);
- return 0;
- }
- /* re-establishment, continue below */
- lapd_stop_t200(dl);
- break;
- case LAPD_STATE_MF_EST:
- LOGDL(dl, LOGL_INFO, "SABM command, multiple frame established state\n");
- /* If link is lost on the remote side, we start over
- * and send DL-ESTABLISH indication again. */
- /* Additionally, continue in case of content resoltion
- * (GSM network). This happens, if the mobile has not
- * yet received UA or another mobile (collision) tries
- * to establish connection. The mobile must receive
- * UA again. */
- /* 5.4.2.1 */
- if (!length) {
- /* If no content resolution, this is a
- * re-establishment. */
- LOGDL(dl, LOGL_INFO, "Remote reestablish\n");
- break;
- }
- if (!dl->cont_res) {
- LOGDL(dl, LOGL_INFO, "SABM command not allowed in state %s\n",
- lapd_state_name(dl->state));
- mdl_error(MDL_CAUSE_SABM_MF, lctx);
- msgb_free(msg);
- return 0;
- }
- /* Ignore SABM if content differs from first SABM. */
- if (dl->mode == LAPD_MODE_NETWORK && length) {
-#ifdef TEST_CONTENT_RESOLUTION_NETWORK
- dl->cont_res->data[0] ^= 0x01;
-#endif
- if (memcmp(dl->cont_res->data, msg->data,
- length)) {
- LOGDL(dl, LOGL_INFO, "Another SABM with different content - "
- "ignoring!\n");
- msgb_free(msg);
- return 0;
- }
- }
- /* send UA again */
- lapd_send_ua(lctx, length, msg->l3h);
- msgb_free(msg);
- return 0;
- case LAPD_STATE_DISC_SENT:
- /* 5.4.6.2 send DM with F=P */
- lapd_send_dm(lctx);
- /* stop Timer T200 */
- lapd_stop_t200(dl);
- msgb_free(msg);
- return send_dl_simple(prim, op, lctx);
- default:
- /* collision: Send UA, but still wait for rx UA, then
- * change to MF_EST state.
- */
- /* check for contention resoultion */
- if (dl->tx_hist[0].msg && dl->tx_hist[0].msg->len) {
- LOGDL(dl, LOGL_NOTICE, "SABM not allowed during contention "
- "resolution (state=%s)\n", lapd_state_name(dl->state));
- mdl_error(MDL_CAUSE_SABM_INFO_NOTALL, lctx);
- }
- lapd_send_ua(lctx, length, msg->l3h);
- msgb_free(msg);
- return 0;
- }
- /* save message context for further use */
- memcpy(&dl->lctx, lctx, sizeof(dl->lctx));
-#ifndef TEST_CONTENT_RESOLUTION_NETWORK
- /* send UA response */
- lapd_send_ua(lctx, length, msg->l3h);
-#endif
- /* set Vs, Vr and Va to 0 */
- dl->v_send = dl->v_recv = dl->v_ack = 0;
- /* clear tx_hist */
- lapd_dl_flush_hist(dl);
- /* enter multiple-frame-established state */
- lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
- /* store content resolution data on network side
- * Note: cont_res will be removed when changing state again,
- * so it must be allocated AFTER lapd_dl_newstate(). */
- if (dl->mode == LAPD_MODE_NETWORK && length) {
- dl->cont_res = lapd_msgb_alloc(length, "CONT RES");
- memcpy(msgb_put(dl->cont_res, length), msg->l3h,
- length);
- LOGDL(dl, LOGL_INFO, "Store content res.\n");
- }
- /* send notification to L3 */
- if (length == 0) {
- /* 5.4.1.2 Normal establishment procedures */
- rc = send_dl_simple(prim, op, lctx);
- msgb_free(msg);
- } else {
- /* 5.4.1.4 Contention resolution establishment */
- msgb_trim(msg, length);
- rc = send_dl_l3(prim, op, lctx, msg);
- }
- return rc;
-}
-
-/* Receive a LAPD U DM message from L1 */
-static int lapd_rx_u_dm(struct msgb *msg, struct lapd_msg_ctx *lctx)
-{
- struct lapd_datalink *dl = lctx->dl;
- int rc = 0;
-
- LOGDL(dl, LOGL_INFO, "DM received in state %s\n", lapd_state_name(dl->state));
- /* G.2.2 Wrong value of the C/R bit */
- if (lctx->cr == dl->cr.rem2loc.cmd) {
- LOGDL(dl, LOGL_ERROR, "DM command error\n");
- msgb_free(msg);
- mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
- return -EINVAL;
- }
- if (!lctx->p_f) {
- /* 5.4.1.2 DM responses with the F bit set to "0"
- * shall be ignored.
- */
- msgb_free(msg);
- return 0;
- }
- switch (dl->state) {
- case LAPD_STATE_SABM_SENT:
- break;
- case LAPD_STATE_MF_EST:
- if (lctx->p_f) {
- LOGDL(dl, LOGL_INFO, "unsolicited DM response\n");
- mdl_error(MDL_CAUSE_UNSOL_DM_RESP, lctx);
- } else {
- LOGDL(dl, LOGL_INFO, "unsolicited DM response, "
- "multiple frame established state\n");
- mdl_error(MDL_CAUSE_UNSOL_DM_RESP_MF, lctx);
- /* reestablish */
- if (!dl->reestablish) {
- msgb_free(msg);
- return 0;
- }
- LOGDL(dl, LOGL_NOTICE, "Performing reestablishment\n");
- lapd_reestablish(dl);
- }
- msgb_free(msg);
- return 0;
- case LAPD_STATE_TIMER_RECOV:
- /* FP = 0 (DM is normal in case PF = 1) */
- if (!lctx->p_f) {
- LOGDL(dl, LOGL_INFO, "unsolicited DM response, multiple frame "
- "established state\n");
- mdl_error(MDL_CAUSE_UNSOL_DM_RESP_MF, lctx);
- msgb_free(msg);
- /* reestablish */
- if (!dl->reestablish)
- return 0;
- LOGDL(dl, LOGL_NOTICE, "Performing reestablishment\n");
- return lapd_reestablish(dl);
- }
- break;
- case LAPD_STATE_DISC_SENT:
- /* stop Timer T200 */
- lapd_stop_t200(dl);
- /* go to idle state */
- lapd_dl_flush_tx(dl);
- lapd_dl_flush_send(dl);
- lapd_dl_newstate(dl, LAPD_STATE_IDLE);
- rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, lctx);
- msgb_free(msg);
- return 0;
- case LAPD_STATE_IDLE:
- /* 5.4.5 all other frame types shall be discarded */
- default:
- LOGDL(dl, LOGL_INFO, "unsolicited DM response! (discarding)\n");
- msgb_free(msg);
- return 0;
- }
- /* stop timer T200 */
- lapd_stop_t200(dl);
- /* go to idle state */
- lapd_dl_newstate(dl, LAPD_STATE_IDLE);
- rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, lctx);
- msgb_free(msg);
- return rc;
-}
-
-/* Receive a LAPD U UI message from L1 */
-static int lapd_rx_u_ui(struct msgb *msg, struct lapd_msg_ctx *lctx)
-{
- struct lapd_datalink *dl = lctx->dl;
- int length = lctx->length;
-
- LOGDL(dl, LOGL_INFO, "UI received\n");
- /* G.2.2 Wrong value of the C/R bit */
- if (lctx->cr == dl->cr.rem2loc.resp) {
- LOGDL(dl, LOGL_ERROR, "UI indicates response error\n");
- msgb_free(msg);
- mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
- return -EINVAL;
- }
-
- /* G.4.5 If UI is received with L>N201 or with M bit
- * set, AN MDL-ERROR-INDICATION is sent to MM.
- */
- if (length > lctx->n201 || lctx->more) {
- LOGDL(dl, LOGL_ERROR, "UI too large error (%d > N201(%d) or M=%d)\n",
- length, lctx->n201, lctx->more);
- msgb_free(msg);
- mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
- return -EIO;
- }
-
- /* do some length checks */
- if (length == 0) {
- /* 5.3.3 UI frames received with the length indicator
- * set to "0" shall be ignored
- */
- LOGDL(dl, LOGL_INFO, "length=0 (discarding)\n");
- msgb_free(msg);
- return 0;
- }
- msgb_trim(msg, length);
- return send_dl_l3(PRIM_DL_UNIT_DATA, PRIM_OP_INDICATION, lctx, msg);
-}
-
-/* Receive a LAPD U DISC message from L1 */
-static int lapd_rx_u_disc(struct msgb *msg, struct lapd_msg_ctx *lctx)
-{
- struct lapd_datalink *dl = lctx->dl;
- int length = lctx->length;
- int rc = 0;
- uint8_t prim, op;
-
- prim = PRIM_DL_REL;
- op = PRIM_OP_INDICATION;
-
- LOGDL(dl, LOGL_INFO, "DISC received in state %s\n", lapd_state_name(dl->state));
- /* flush tx and send buffers */
- lapd_dl_flush_tx(dl);
- lapd_dl_flush_send(dl);
- /* 5.7.1 */
- dl->seq_err_cond = 0;
- /* G.2.2 Wrong value of the C/R bit */
- if (lctx->cr == dl->cr.rem2loc.resp) {
- LOGDL(dl, LOGL_ERROR, "DISC response error\n");
- msgb_free(msg);
- mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
- return -EINVAL;
- }
- if (length > 0 || lctx->more) {
- /* G.4.4 If a DISC or DM frame is received with L>0 or
- * with the M bit set to "1", an MDL-ERROR-INDICATION
- * primitive with cause "U frame with incorrect
- * parameters" is sent to the mobile management entity.
- */
- LOGDL(dl, LOGL_ERROR, "U frame iwth incorrect parameters\n");
- msgb_free(msg);
- mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
- return -EIO;
- }
- switch (dl->state) {
- case LAPD_STATE_IDLE:
- LOGDL(dl, LOGL_INFO, "DISC in idle state\n");
- /* send DM with F=P */
- msgb_free(msg);
- return lapd_send_dm(lctx);
- case LAPD_STATE_SABM_SENT:
- LOGDL(dl, LOGL_INFO, "DISC in SABM state\n");
- /* 5.4.6.2 send DM with F=P */
- lapd_send_dm(lctx);
- /* stop Timer T200 */
- lapd_stop_t200(dl);
- /* go to idle state */
- lapd_dl_newstate(dl, LAPD_STATE_IDLE);
- msgb_free(msg);
- return send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION,
- lctx);
- case LAPD_STATE_MF_EST:
- case LAPD_STATE_TIMER_RECOV:
- LOGDL(dl, LOGL_INFO, "DISC in est state\n");
- break;
- case LAPD_STATE_DISC_SENT:
- LOGDL(dl, LOGL_INFO, "DISC in disc state\n");
- prim = PRIM_DL_REL;
- op = PRIM_OP_CONFIRM;
- break;
- default:
- lapd_send_ua(lctx, length, msg->l3h);
- msgb_free(msg);
- return 0;
- }
- /* send UA response */
- lapd_send_ua(lctx, length, msg->l3h);
- /* stop Timer T200 */
- lapd_stop_t200(dl);
- /* enter idle state, keep tx-buffer with UA response */
- lapd_dl_newstate(dl, LAPD_STATE_IDLE);
- /* send notification to L3 */
- rc = send_dl_simple(prim, op, lctx);
- msgb_free(msg);
- return rc;
-}
-
-/* Receive a LAPD U UA message from L1 */
-static int lapd_rx_u_ua(struct msgb *msg, struct lapd_msg_ctx *lctx)
-{
- struct lapd_datalink *dl = lctx->dl;
- int length = lctx->length;
- int rc = 0;
-
- LOGDL(dl, LOGL_INFO, "UA received in state %s\n", lapd_state_name(dl->state));
- /* G.2.2 Wrong value of the C/R bit */
- if (lctx->cr == dl->cr.rem2loc.cmd) {
- LOGDL(dl, LOGL_ERROR, "UA indicates command error\n");
- msgb_free(msg);
- mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
- return -EINVAL;
- }
-
- /* G.4.5 If UA is received with L>N201 or with M bit
- * set, AN MDL-ERROR-INDICATION is sent to MM.
- */
- if (lctx->more || length > lctx->n201) {
- LOGDL(dl, LOGL_ERROR, "UA too large error\n");
- msgb_free(msg);
- mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
- return -EIO;
- }
-
- if (!lctx->p_f) {
- /* 5.4.1.2 A UA response with the F bit set to "0"
- * shall be ignored.
- */
- LOGDL(dl, LOGL_INFO, "F=0 (discarding)\n");
- msgb_free(msg);
- return 0;
- }
- switch (dl->state) {
- case LAPD_STATE_SABM_SENT:
- break;
- case LAPD_STATE_MF_EST:
- case LAPD_STATE_TIMER_RECOV:
- LOGDL(dl, LOGL_INFO, "unsolicited UA response! (discarding)\n");
- mdl_error(MDL_CAUSE_UNSOL_UA_RESP, lctx);
- msgb_free(msg);
- return 0;
- case LAPD_STATE_DISC_SENT:
- LOGDL(dl, LOGL_INFO, "UA in disconnect state\n");
- /* stop Timer T200 */
- lapd_stop_t200(dl);
- /* go to idle state */
- lapd_dl_flush_tx(dl);
- lapd_dl_flush_send(dl);
- lapd_dl_newstate(dl, LAPD_STATE_IDLE);
- rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, lctx);
- msgb_free(msg);
- return 0;
- case LAPD_STATE_IDLE:
- /* 5.4.5 all other frame types shall be discarded */
- default:
- LOGDL(dl, LOGL_INFO, "unsolicited UA response! (discarding)\n");
- msgb_free(msg);
- return 0;
- }
- LOGDL(dl, LOGL_INFO, "UA in SABM state\n");
- /* stop Timer T200 */
- lapd_stop_t200(dl);
- /* compare UA with SABME if contention resolution is applied */
- if (dl->tx_hist[0].msg->len) {
- if (length != (dl->tx_hist[0].msg->len)
- || !!memcmp(dl->tx_hist[0].msg->data, msg->l3h,
- length)) {
- LOGDL(dl, LOGL_INFO, "**** UA response mismatches ****\n");
- /* go to idle state */
- lapd_dl_flush_tx(dl);
- lapd_dl_flush_send(dl);
- lapd_dl_newstate(dl, LAPD_STATE_IDLE);
- rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, lctx);
- msgb_free(msg);
- return 0;
- }
- }
- /* set Vs, Vr and Va to 0 */
- dl->v_send = dl->v_recv = dl->v_ack = 0;
- /* clear tx_hist */
- lapd_dl_flush_hist(dl);
- /* enter multiple-frame-established state */
- lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
- /* send outstanding frames, if any (resume / reconnect) */
- lapd_send_i(lctx, __LINE__);
- /* send notification to L3 */
- rc = send_dl_simple(PRIM_DL_EST, PRIM_OP_CONFIRM, lctx);
- msgb_free(msg);
- return rc;
-}
-
-/* Receive a LAPD U FRMR message from L1 */
-static int lapd_rx_u_frmr(struct msgb *msg, struct lapd_msg_ctx *lctx)
-{
- struct lapd_datalink *dl = lctx->dl;
-
- LOGDL(dl, LOGL_NOTICE, "Frame reject received\n");
- /* send MDL ERROR INIDCATION to L3 */
- mdl_error(MDL_CAUSE_FRMR, lctx);
- msgb_free(msg);
- /* reestablish */
- if (!dl->reestablish)
- return 0;
- LOGDL(dl, LOGL_NOTICE, "Performing reestablishment\n");
- return lapd_reestablish(dl);
-}
-
-/* Receive a LAPD U (Unnumbered) message from L1 */
-static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx)
-{
- switch (lctx->s_u) {
- case LAPD_U_SABM:
- case LAPD_U_SABME:
- return lapd_rx_u_sabm(msg, lctx);
- case LAPD_U_DM:
- return lapd_rx_u_dm(msg, lctx);
- case LAPD_U_UI:
- return lapd_rx_u_ui(msg, lctx);
- case LAPD_U_DISC:
- return lapd_rx_u_disc(msg, lctx);
- case LAPD_U_UA:
- return lapd_rx_u_ua(msg, lctx);
- case LAPD_U_FRMR:
- return lapd_rx_u_frmr(msg, lctx);
- default:
- /* G.3.1 */
- LOGDL(lctx->dl, LOGL_NOTICE, "Unnumbered frame not allowed\n");
- msgb_free(msg);
- mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
- return -EINVAL;
- }
-}
-
-/* Receive a LAPD S (Supervisory) message from L1 */
-static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
-{
- struct lapd_datalink *dl = lctx->dl;
- int length = lctx->length;
-
- if (length > 0 || lctx->more) {
- /* G.4.3 If a supervisory frame is received with L>0 or
- * with the M bit set to "1", an MDL-ERROR-INDICATION
- * primitive with cause "S frame with incorrect
- * parameters" is sent to the mobile management entity. */
- LOGDL(dl, LOGL_ERROR, "S frame with incorrect parameters\n");
- msgb_free(msg);
- mdl_error(MDL_CAUSE_SFRM_INC_PARAM, lctx);
- return -EIO;
- }
-
- if (lctx->cr == dl->cr.rem2loc.resp
- && lctx->p_f
- && dl->state != LAPD_STATE_TIMER_RECOV) {
- /* 5.4.2.2: Inidcate error on supervisory reponse F=1 */
- LOGDL(dl, LOGL_NOTICE, "S frame response with F=1 error\n");
- mdl_error(MDL_CAUSE_UNSOL_SPRV_RESP, lctx);
- }
-
- switch (dl->state) {
- case LAPD_STATE_IDLE:
- /* if P=1, respond DM with F=1 (5.2.2) */
- /* 5.4.5 all other frame types shall be discarded */
- if (lctx->p_f)
- lapd_send_dm(lctx); /* F=P */
- /* fall though */
- case LAPD_STATE_SABM_SENT:
- case LAPD_STATE_DISC_SENT:
- LOGDL(dl, LOGL_NOTICE, "S frame ignored in this state\n");
- msgb_free(msg);
- return 0;
- }
- switch (lctx->s_u) {
- case LAPD_S_RR:
- LOGDL(dl, LOGL_INFO, "RR received in state %s\n", lapd_state_name(dl->state));
- /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */
- lapd_acknowledge(lctx);
-
- /* 5.5.3.2 */
- if (lctx->cr == dl->cr.rem2loc.cmd
- && lctx->p_f) {
- if (!dl->own_busy && !dl->seq_err_cond) {
- LOGDL(dl, LOGL_INFO, "RR frame command with polling bit set and "
- "we are not busy, so we reply with RR frame response\n");
- lapd_send_rr(lctx, 1, 0);
- /* NOTE: In case of sequence error condition,
- * the REJ frame has been transmitted when
- * entering the condition, so it has not be
- * done here
- */
- } else if (dl->own_busy) {
- LOGDL(dl, LOGL_INFO, "RR frame command with polling bit set and "
- "we are busy, so we reply with RR frame response\n");
- lapd_send_rnr(lctx, 1, 0);
- }
- } else if (lctx->cr == dl->cr.rem2loc.resp
- && lctx->p_f
- && dl->state == LAPD_STATE_TIMER_RECOV) {
- LOGDL(dl, LOGL_INFO, "RR response with F==1, and we are in timer recovery "
- "state, so we leave that state\n");
- /* V(S) to the N(R) in the RR frame */
- dl->v_send = lctx->n_recv;
- /* stop Timer T200 */
- lapd_stop_t200(dl);
- /* 5.5.7 Clear timer recovery condition */
- lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
- }
- /* Send message, if possible due to acknowledged data */
- lapd_send_i(lctx, __LINE__);
-
- break;
- case LAPD_S_RNR:
- LOGDL(dl, LOGL_INFO, "RNR received in state %s\n", lapd_state_name(dl->state));
- /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */
- lapd_acknowledge(lctx);
-
- /* 5.5.5 */
- /* Set peer receiver busy condition */
- dl->peer_busy = 1;
-
- if (lctx->p_f) {
- if (lctx->cr == dl->cr.rem2loc.cmd) {
- if (!dl->own_busy) {
- LOGDL(dl, LOGL_INFO, "RNR poll command and we are not busy, "
- "so we reply with RR final response\n");
- /* Send RR with F=1 */
- lapd_send_rr(lctx, 1, 0);
- } else {
- LOGDL(dl, LOGL_INFO, "RNR poll command and we are busy, so "
- "we reply with RNR final response\n");
- /* Send RNR with F=1 */
- lapd_send_rnr(lctx, 1, 0);
- }
- } else if (dl->state == LAPD_STATE_TIMER_RECOV) {
- LOGDL(dl, LOGL_INFO, "RNR poll response and we in timer recovery "
- "state, so we leave that state\n");
- /* 5.5.7 Clear timer recovery condition */
- lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
- /* V(S) to the N(R) in the RNR frame */
- dl->v_send = lctx->n_recv;
- }
- } else
- LOGDL(dl, LOGL_INFO, "RNR not polling/final state received\n");
-
- /* Send message, if possible due to acknowledged data */
- lapd_send_i(lctx, __LINE__);
-
- break;
- case LAPD_S_REJ:
- LOGDL(dl, LOGL_INFO, "REJ received in state %s\n", lapd_state_name(dl->state));
- /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */
- lapd_acknowledge(lctx);
-
- /* 5.5.4.1 */
- if (dl->state != LAPD_STATE_TIMER_RECOV) {
- /* Clear an existing peer receiver busy condition */
- dl->peer_busy = 0;
- /* V(S) and V(A) to the N(R) in the REJ frame */
- dl->v_send = dl->v_ack = lctx->n_recv;
- /* stop Timer T200 */
- lapd_stop_t200(dl);
- /* 5.5.3.2 */
- if (lctx->cr == dl->cr.rem2loc.cmd && lctx->p_f) {
- if (!dl->own_busy && !dl->seq_err_cond) {
- LOGDL(dl, LOGL_INFO, "REJ poll command not in timer recovery "
- "state and not in own busy condition received, so we "
- "respond with RR final response\n");
- lapd_send_rr(lctx, 1, 0);
- /* NOTE: In case of sequence error
- * condition, the REJ frame has been
- * transmitted when entering the
- * condition, so it has not be done
- * here
- */
- } else if (dl->own_busy) {
- LOGDL(dl, LOGL_INFO, "REJ poll command not in timer recovery "
- "state and in own busy condition received, so we "
- "respond with RNR final response\n");
- lapd_send_rnr(lctx, 1, 0);
- }
- } else
- LOGDL(dl, LOGL_INFO, "REJ response or not polling command not "
- "in timer recovery state received\n");
- /* send MDL ERROR INIDCATION to L3 */
- if (lctx->cr == dl->cr.rem2loc.resp && lctx->p_f) {
- LOGDL(dl, LOGL_ERROR, "unsolicited supervisory response!\n");
- mdl_error(MDL_CAUSE_UNSOL_SPRV_RESP, lctx);
- }
-
- } else if (lctx->cr == dl->cr.rem2loc.resp && lctx->p_f) {
- LOGDL(dl, LOGL_INFO, "REJ poll response in timer recovery state received\n");
- /* Clear an existing peer receiver busy condition */
- dl->peer_busy = 0;
- /* V(S) and V(A) to the N(R) in the REJ frame */
- dl->v_send = dl->v_ack = lctx->n_recv;
- /* stop Timer T200 */
- lapd_stop_t200(dl);
- /* 5.5.7 Clear timer recovery condition */
- lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
- } else {
- /* Clear an existing peer receiver busy condition */
- dl->peer_busy = 0;
- /* V(S) and V(A) to the N(R) in the REJ frame */
- dl->v_send = dl->v_ack = lctx->n_recv;
- /* 5.5.3.2 */
- if (lctx->cr == dl->cr.rem2loc.cmd && lctx->p_f) {
- if (!dl->own_busy && !dl->seq_err_cond) {
- LOGDL(dl, LOGL_INFO, "REJ poll command in timer recovery "
- "state and not in own busy condition received, so we "
- "respond with RR final response\n");
- lapd_send_rr(lctx, 1, 0);
- /* NOTE: In case of sequence error
- * condition, the REJ frame has been
- * transmitted when entering the
- * condition, so it has not be done
- * here
- */
- } else if (dl->own_busy) {
- LOGDL(dl, LOGL_INFO, "REJ poll command in timer recovery "
- "state and in own busy condition received, so we "
- "respond with RNR final response\n");
- lapd_send_rnr(lctx, 1, 0);
- }
- } else
- LOGDL(dl, LOGL_INFO, "REJ response or not polling command in "
- "timer recovery state received\n");
- }
-
- /* FIXME: 5.5.4.2 2) */
-
- /* Send message, if possible due to acknowledged data */
- lapd_send_i(lctx, __LINE__);
-
- break;
- default:
- /* G.3.1 */
- LOGDL(dl, LOGL_ERROR, "Supervisory frame not allowed\n");
- msgb_free(msg);
- mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
- return -EINVAL;
- }
- msgb_free(msg);
- return 0;
-}
-
-/* Receive a LAPD I (Information) message from L1 */
-static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
-{
- struct lapd_datalink *dl = lctx->dl;
- //uint8_t nr = lctx->n_recv;
- uint8_t ns = lctx->n_send;
- int length = lctx->length;
- int rc;
-
- LOGDL(dl, LOGL_INFO, "I received in state %s on SAPI(%u)\n",
- lapd_state_name(dl->state), lctx->sapi);
-
- /* G.2.2 Wrong value of the C/R bit */
- if (lctx->cr == dl->cr.rem2loc.resp) {
- LOGDL(dl, LOGL_ERROR, "I frame response not allowed (state %s)\n",
- lapd_state_name(dl->state));
- msgb_free(msg);
- mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
- return -EINVAL;
- }
-
- if (length == 0 || length > lctx->n201) {
- /* G.4.2 If the length indicator of an I frame is set
- * to a numerical value L>N201 or L=0, an MDL-ERROR-INDICATION
- * primitive with cause "I frame with incorrect length"
- * is sent to the mobile management entity. */
- LOGDL(dl, LOGL_ERROR, "I frame length not allowed (state %s)\n",
- lapd_state_name(dl->state));
- msgb_free(msg);
- mdl_error(MDL_CAUSE_IFRM_INC_LEN, lctx);
- return -EIO;
- }
-
- /* G.4.2 If the numerical value of L is L<N201 and the M
- * bit is set to "1", then an MDL-ERROR-INDICATION primitive with
- * cause "I frame with incorrect use of M bit" is sent to the
- * mobile management entity. */
- if (lctx->more && length < lctx->n201) {
- LOGDL(dl, LOGL_ERROR, "I frame with M bit too short (state %s)\n",
- lapd_state_name(dl->state));
- msgb_free(msg);
- mdl_error(MDL_CAUSE_IFRM_INC_MBITS, lctx);
- return -EIO;
- }
-
- switch (dl->state) {
- case LAPD_STATE_IDLE:
- /* if P=1, respond DM with F=1 (5.2.2) */
- /* 5.4.5 all other frame types shall be discarded */
- if (lctx->p_f)
- lapd_send_dm(lctx); /* F=P */
- /* fall though */
- case LAPD_STATE_SABM_SENT:
- case LAPD_STATE_DISC_SENT:
- LOGDL(dl, LOGL_NOTICE, "I frame ignored in state %s\n", lapd_state_name(dl->state));
- msgb_free(msg);
- return 0;
- }
-
- /* 5.7.1: N(s) sequence error */
- if (ns != dl->v_recv) {
- LOGDL(dl, LOGL_NOTICE, "N(S) sequence error: N(S)=%u, V(R)=%u (state %s)\n",
- ns, dl->v_recv, lapd_state_name(dl->state));
- /* discard data */
- msgb_free(msg);
- if (dl->seq_err_cond != 1) {
- /* FIXME: help me understand what exactly todo here
- */
- dl->seq_err_cond = 1;
- lapd_send_rej(lctx, lctx->p_f);
- } else {
- /* If there are two subsequent sequence errors received,
- * ignore it. (Ignore every second subsequent error.)
- * This happens if our reply with the REJ is too slow,
- * so the remote gets a T200 timeout and sends another
- * frame with a sequence error.
- * Test showed that replying with two subsequent REJ
- * messages could the remote L2 process to abort.
- * Replying too slow shouldn't happen, but may happen
- * over serial link between BB and LAPD.
- */
- dl->seq_err_cond = 2;
- }
- /* Even if N(s) sequence error, acknowledge to N(R)-1 */
- /* 5.5.3.1: Acknowlege all transmitted frames up the N(R)-1 */
- lapd_acknowledge(lctx); /* V(A) is also set here */
-
- /* Send message, if possible due to acknowledged data */
- lapd_send_i(lctx, __LINE__);
-
- return 0;
- }
- dl->seq_err_cond = 0;
-
- /* Increment receiver state */
- dl->v_recv = inc_mod(dl->v_recv, dl->v_range);
- LOGDL(dl, LOGL_INFO, "incrementing V(R) to %u\n", dl->v_recv);
-
- /* 5.5.3.1: Acknowlege all transmitted frames up the the N(R)-1 */
- lapd_acknowledge(lctx); /* V(A) is also set here */
-
- /* Only if we are not in own receiver busy condition */
- if (!dl->own_busy) {
- /* if the frame carries a complete segment */
- if (!lctx->more && !dl->rcv_buffer) {
- LOGDL(dl, LOGL_INFO, "message in single I frame\n");
- /* send a DATA INDICATION to L3 */
- msgb_trim(msg, length);
- rc = send_dl_l3(PRIM_DL_DATA, PRIM_OP_INDICATION, lctx,
- msg);
- } else {
- /* create rcv_buffer */
- if (!dl->rcv_buffer) {
- LOGDL(dl, LOGL_INFO, "message in multiple I frames (first message)\n");
- dl->rcv_buffer = lapd_msgb_alloc(dl->maxf,
- "LAPD RX");
- dl->rcv_buffer->l3h = dl->rcv_buffer->data;
- }
- /* concat. rcv_buffer */
- if (msgb_l3len(dl->rcv_buffer) + length > dl->maxf) {
- LOGDL(dl, LOGL_NOTICE, "Received frame overflow!\n");
- } else {
- memcpy(msgb_put(dl->rcv_buffer, length),
- msg->l3h, length);
- }
- /* if the last segment was received */
- if (!lctx->more) {
- LOGDL(dl, LOGL_INFO, "message in multiple I frames (last message)\n");
- rc = send_dl_l3(PRIM_DL_DATA,
- PRIM_OP_INDICATION, lctx,
- dl->rcv_buffer);
- dl->rcv_buffer = NULL;
- } else
- LOGDL(dl, LOGL_INFO, "message in multiple I frames (next message)\n");
- msgb_free(msg);
-
- }
- /* the L3 or higher (called in-line above via send_dl_l3) might have destroyed the
- * data link meanwhile. See OS#1761 */
- if (dl->state == LAPD_STATE_NULL)
- return 0;
- } else
- LOGDL(dl, LOGL_INFO, "I frame ignored during own receiver busy condition\n");
-
- /* Check for P bit */
- if (lctx->p_f) {
- /* 5.5.2.1 */
- /* check if we are not in own receiver busy */
- if (!dl->own_busy) {
- LOGDL(dl, LOGL_INFO, "we are not busy, send RR\n");
- /* Send RR with F=1 */
- rc = lapd_send_rr(lctx, 1, 0);
- } else {
- LOGDL(dl, LOGL_INFO, "we are busy, send RNR\n");
- /* Send RNR with F=1 */
- rc = lapd_send_rnr(lctx, 1, 0);
- }
- } else {
- /* 5.5.2.2 */
- /* check if we are not in own receiver busy */
- if (!dl->own_busy) {
- /* NOTE: V(R) is already set above */
- rc = lapd_send_i(lctx, __LINE__);
-
- /* if update_pending_iframe returns 0 it updated
- * the lapd header of an iframe in the tx queue */
- if (rc && dl->update_pending_frames)
- rc = dl->update_pending_frames(lctx);
-
- if (rc) {
- LOGDL(dl, LOGL_INFO, "we are not busy and have no pending data, "
- "send RR\n");
- /* Send RR with F=0 */
- return lapd_send_rr(lctx, 0, 0);
- }
- /* all I or one RR is sent, we are done */
- return 0;
- } else {
- LOGDL(dl, LOGL_INFO, "we are busy, send RNR\n");
- /* Send RNR with F=0 */
- rc = lapd_send_rnr(lctx, 0, 0);
- }
- }
-
- /* Send message, if possible due to acknowledged data */
- lapd_send_i(lctx, __LINE__);
-
- return rc;
-}
-
-/* Receive a LAPD message from L1 */
-int lapd_ph_data_ind(struct msgb *msg, struct lapd_msg_ctx *lctx)
-{
- int rc;
-
- switch (lctx->format) {
- case LAPD_FORM_U:
- rc = lapd_rx_u(msg, lctx);
- break;
- case LAPD_FORM_S:
- rc = lapd_rx_s(msg, lctx);
- break;
- case LAPD_FORM_I:
- rc = lapd_rx_i(msg, lctx);
- break;
- default:
- LOGDL(lctx->dl, LOGL_NOTICE, "unknown LAPD format 0x%02x\n", lctx->format);
- msgb_free(msg);
- rc = -EINVAL;
- }
- return rc;
-}
-
-/* L3 -> L2 */
-
-/* send unit data */
-static int lapd_udata_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
-{
- struct lapd_datalink *dl = lctx->dl;
- struct msgb *msg = dp->oph.msg;
- struct lapd_msg_ctx nctx;
-
- memcpy(&nctx, lctx, sizeof(nctx));
- /* keep nctx.ldp */
- /* keep nctx.sapi */
- /* keep nctx.tei */
- nctx.cr = dl->cr.loc2rem.cmd;
- nctx.format = LAPD_FORM_U;
- nctx.s_u = LAPD_U_UI;
- /* keep nctx.p_f */
- nctx.length = msg->len;
- nctx.more = 0;
-
- return dl->send_ph_data_req(&nctx, msg);
-}
-
-static void msg_to_tx_hist(struct lapd_history *tx_hist, const struct msgb *msg, int length, int more)
-{
- tx_hist->msg = lapd_msgb_alloc(msg->len, "HIST");
- tx_hist->more = more;
- msgb_put(tx_hist->msg, msg->len);
- if (length)
- memcpy(tx_hist->msg->data, msg->l3h, msg->len);
-}
-
-static void msg_to_tx_hist0(struct lapd_datalink *dl, const struct msgb *msg)
-{
- return msg_to_tx_hist(&dl->tx_hist[0], msg, msg->len, 0);
-}
-
-/* request link establishment */
-static int lapd_est_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
-{
- struct lapd_datalink *dl = lctx->dl;
- struct msgb *msg = dp->oph.msg;
- struct lapd_msg_ctx nctx;
-
- if (msg->len)
- LOGDL(dl, LOGL_INFO, "perform establishment with content (SABM)\n");
- else
- LOGDL(dl, LOGL_INFO, "perform normal establishm. (SABM)\n");
-
- /* Flush send-queue */
- /* Clear send-buffer */
- lapd_dl_flush_send(dl);
- /* be sure that history is empty */
- lapd_dl_flush_hist(dl);
-
- /* save message context for further use */
- memcpy(&dl->lctx, lctx, sizeof(dl->lctx));
-
- /* Discard partly received L3 message */
- msgb_free(dl->rcv_buffer);
- dl->rcv_buffer = NULL;
-
- /* assemble message */
- memcpy(&nctx, &dl->lctx, sizeof(nctx));
- /* keep nctx.ldp */
- /* keep nctx.sapi */
- /* keep nctx.tei */
- nctx.cr = dl->cr.loc2rem.cmd;
- nctx.format = LAPD_FORM_U;
- nctx.s_u = (dl->use_sabme) ? LAPD_U_SABME : LAPD_U_SABM;
- nctx.p_f = 1;
- nctx.length = msg->len;
- nctx.more = 0;
-
- /* Transmit-buffer carries exactly one segment */
- msg_to_tx_hist0(dl, msg);
-
- /* set Vs to 0, because it is used as index when resending SABM */
- dl->v_send = 0;
-
- /* Set states */
- dl->own_busy = dl->peer_busy = 0;
- dl->retrans_ctr = 0;
- lapd_dl_newstate(dl, LAPD_STATE_SABM_SENT);
-
- /* Tramsmit and start T200 */
- dl->send_ph_data_req(&nctx, msg);
- lapd_start_t200(dl);
-
- return 0;
-}
-
-/* send data */
-static int lapd_data_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
-{
- struct lapd_datalink *dl = lctx->dl;
- struct msgb *msg = dp->oph.msg;
-
- if (msgb_l3len(msg) == 0) {
- LOGDL(dl, LOGL_ERROR, "writing an empty message is not possible\n");
- msgb_free(msg);
- return -1;
- }
-
- LOGDL(dl, LOGL_INFO, "writing message to send-queue: l3len: %d\n", msgb_l3len(msg));
-
- /* Write data into the send queue */
- msgb_enqueue(&dl->send_queue, msg);
-
- /* Send message, if possible */
- lapd_send_i(&dl->lctx, __LINE__);
-
- return 0;
-}
-
-/* Send next I frame from queued/buffered data */
-static int lapd_send_i(struct lapd_msg_ctx *lctx, int line)
-{
- struct lapd_datalink *dl = lctx->dl;
- uint8_t k = dl->k;
- uint8_t h;
- struct msgb *msg;
- int length, left;
- int rc = - 1; /* we sent nothing */
- struct lapd_msg_ctx nctx;
-
-
- LOGDL(dl, LOGL_INFO, "%s() called from line %d\n", __func__, line);
-
- next_frame:
-
- if (dl->peer_busy) {
- LOGDL(dl, LOGL_INFO, "peer busy, not sending\n");
- return rc;
- }
-
- if (dl->state == LAPD_STATE_TIMER_RECOV) {
- LOGDL(dl, LOGL_INFO, "timer recovery, not sending\n");
- return rc;
- }
-
- /* If the send state variable V(S) is equal to V(A) plus k
- * (where k is the maximum number of outstanding I frames - see
- * subclause 5.8.4), the data link layer entity shall not transmit any
- * new I frames, but shall retransmit an I frame as a result
- * of the error recovery procedures as described in subclauses 5.5.4 and
- * 5.5.7. */
- if (dl->v_send == add_mod(dl->v_ack, k, dl->v_range)) {
- LOGDL(dl, LOGL_INFO, "k frames outstanding, not sending more "
- "(k=%u V(S)=%u V(A)=%u)\n", k, dl->v_send, dl->v_ack);
- return rc;
- }
-
- h = do_mod(dl->v_send, dl->range_hist);
-
- /* if we have no tx_hist yet, we create it */
- if (!dl->tx_hist[h].msg) {
- /* Get next message into send-buffer, if any */
- if (!dl->send_buffer) {
- next_message:
- dl->send_out = 0;
- dl->send_buffer = msgb_dequeue(&dl->send_queue);
- /* No more data to be sent */
- if (!dl->send_buffer)
- return rc;
- LOGDL(dl, LOGL_INFO, "get message from send-queue\n");
- }
-
- /* How much is left in the send-buffer? */
- left = msgb_l3len(dl->send_buffer) - dl->send_out;
- /* Segment, if data exceeds N201 */
- length = left;
- if (length > lctx->n201)
- length = lctx->n201;
- LOGDL(dl, LOGL_INFO, "msg-len %d sent %d left %d N201 %d length %d "
- "first byte %02x\n", msgb_l3len(dl->send_buffer), dl->send_out, left,
- lctx->n201, length, dl->send_buffer->l3h[0]);
- /* If message in send-buffer is completely sent */
- if (left == 0) {
- msgb_free(dl->send_buffer);
- dl->send_buffer = NULL;
- goto next_message;
- }
-
- LOGDL(dl, LOGL_INFO, "send I frame %sV(S)=%d\n",
- (left > length) ? "segment " : "", dl->v_send);
-
- /* Create I frame (segment) and transmit-buffer content */
- msg = lapd_msgb_alloc(length, "LAPD I");
- msg->l3h = msgb_put(msg, length);
- /* assemble message */
- memcpy(&nctx, &dl->lctx, sizeof(nctx));
- /* keep nctx.ldp */
- /* keep nctx.sapi */
- /* keep nctx.tei */
- nctx.cr = dl->cr.loc2rem.cmd;
- nctx.format = LAPD_FORM_I;
- nctx.p_f = 0;
- nctx.n_send = dl->v_send;
- nctx.n_recv = dl->v_recv;
- nctx.length = length;
- if (left > length)
- nctx.more = 1;
- else
- nctx.more = 0;
- if (length)
- memcpy(msg->l3h, dl->send_buffer->l3h + dl->send_out,
- length);
- /* store in tx_hist */
- msg_to_tx_hist(&dl->tx_hist[h], msg, length, nctx.more);
-
- /* Add length to track how much is already in the tx buffer */
- dl->send_out += length;
- } else {
- LOGDL(dl, LOGL_INFO, "resend I frame from tx buffer V(S)=%d\n", dl->v_send);
-
- /* Create I frame (segment) from tx_hist */
- length = dl->tx_hist[h].msg->len;
- msg = lapd_msgb_alloc(length, "LAPD I resend");
- msg->l3h = msgb_put(msg, length);
- /* assemble message */
- memcpy(&nctx, &dl->lctx, sizeof(nctx));
- /* keep nctx.ldp */
- /* keep nctx.sapi */
- /* keep nctx.tei */
- nctx.cr = dl->cr.loc2rem.cmd;
- nctx.format = LAPD_FORM_I;
- nctx.p_f = 0;
- nctx.n_send = dl->v_send;
- nctx.n_recv = dl->v_recv;
- nctx.length = length;
- nctx.more = dl->tx_hist[h].more;
- if (length)
- memcpy(msg->l3h, dl->tx_hist[h].msg->data, length);
- }
-
- /* The value of the send state variable V(S) shall be incremented by 1
- * at the end of the transmission of the I frame */
- dl->v_send = inc_mod(dl->v_send, dl->v_range);
-
- /* 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)) {
- /* stop Timer T203, if running */
- lapd_stop_t203(dl);
- /* start Timer T200 */
- lapd_start_t200(dl);
- }
-
- dl->send_ph_data_req(&nctx, msg);
-
- rc = 0; /* we sent something */
- goto next_frame;
-}
-
-/* request link suspension */
-static int lapd_susp_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
-{
- struct lapd_datalink *dl = lctx->dl;
- struct msgb *msg = dp->oph.msg;
-
- LOGDL(dl, LOGL_INFO, "perform suspension\n");
-
- /* put back the send-buffer to the send-queue (first position) */
- if (dl->send_buffer) {
- LOGDL(dl, LOGL_INFO, "put frame in sendbuffer back to queue\n");
- llist_add(&dl->send_buffer->list, &dl->send_queue);
- dl->send_buffer = NULL;
- } else
- LOGDL(dl, LOGL_INFO, "no frame in sendbuffer\n");
-
- /* Clear transmit buffer, but keep send buffer */
- lapd_dl_flush_tx(dl);
- /* Stop timers (there is no state change, so we must stop all timers */
- lapd_stop_t200(dl);
- lapd_stop_t203(dl);
-
- msgb_free(msg);
-
- return send_dl_simple(PRIM_DL_SUSP, PRIM_OP_CONFIRM, &dl->lctx);
-}
-
-/* request, resume or reconnect of link */
-static int lapd_res_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
-{
- struct lapd_datalink *dl = lctx->dl;
- struct msgb *msg = dp->oph.msg;
- struct lapd_msg_ctx nctx;
-
- LOGDL(dl, LOGL_INFO, "perform re-establishment (SABM) length=%d\n", msg->len);
-
- /* be sure that history is empty */
- lapd_dl_flush_hist(dl);
-
- /* save message context for further use */
- memcpy(&dl->lctx, lctx, sizeof(dl->lctx));
-
- /* Replace message in the send-buffer (reconnect) */
- msgb_free(dl->send_buffer);
- dl->send_buffer = NULL;
-
- dl->send_out = 0;
- if (msg->len) {
- /* Write data into the send buffer, to be sent first */
- dl->send_buffer = msg;
- } else {
- msgb_free(msg);
- msg = NULL;
- dl->send_buffer = NULL;
- }
-
- /* Discard partly received L3 message */
- msgb_free(dl->rcv_buffer);
- dl->rcv_buffer = NULL;
-
- /* Create new msgb (old one is now free) */
- msg = lapd_msgb_alloc(0, "LAPD SABM");
- msg->l3h = msg->data;
- /* assemble message */
- memcpy(&nctx, &dl->lctx, sizeof(nctx));
- /* keep nctx.ldp */
- /* keep nctx.sapi */
- /* keep nctx.tei */
- nctx.cr = dl->cr.loc2rem.cmd;
- nctx.format = LAPD_FORM_U;
- nctx.s_u = (dl->use_sabme) ? LAPD_U_SABME : LAPD_U_SABM;
- nctx.p_f = 1;
- nctx.length = 0;
- nctx.more = 0;
-
- msg_to_tx_hist0(dl, msg);
-
- /* set Vs to 0, because it is used as index when resending SABM */
- dl->v_send = 0;
-
- /* Set states */
- dl->own_busy = dl->peer_busy = 0;
- dl->retrans_ctr = 0;
- lapd_dl_newstate(dl, LAPD_STATE_SABM_SENT);
-
- /* Tramsmit and start T200 */
- dl->send_ph_data_req(&nctx, msg);
- lapd_start_t200(dl);
-
- return 0;
-}
-
-/* requesst release of link */
-static int lapd_rel_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
-{
- struct lapd_datalink *dl = lctx->dl;
- struct msgb *msg = dp->oph.msg;
- struct lapd_msg_ctx nctx;
-
- /* local release */
- if (dp->u.rel_req.mode) {
- LOGDL(dl, LOGL_INFO, "perform local release\n");
- msgb_free(msg);
- /* stop Timer T200 */
- lapd_stop_t200(dl);
- /* enter idle state, T203 is stopped here, if running */
- lapd_dl_newstate(dl, LAPD_STATE_IDLE);
- /* flush buffers */
- lapd_dl_flush_tx(dl);
- lapd_dl_flush_send(dl);
- /* send notification to L3 */
- return send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, &dl->lctx);
- }
-
- /* in case we are already disconnecting */
- if (dl->state == LAPD_STATE_DISC_SENT)
- return -EBUSY;
-
- /* flush tx_hist */
- lapd_dl_flush_hist(dl);
-
- LOGDL(dl, LOGL_INFO, "perform normal release (DISC)\n");
-
- /* Push LAPD header on msgb */
- /* assemble message */
- memcpy(&nctx, &dl->lctx, sizeof(nctx));
- /* keep nctx.ldp */
- /* keep nctx.sapi */
- /* keep nctx.tei */
- nctx.cr = dl->cr.loc2rem.cmd;
- nctx.format = LAPD_FORM_U;
- nctx.s_u = LAPD_U_DISC;
- nctx.p_f = 1;
- nctx.length = 0;
- nctx.more = 0;
-
- msg_to_tx_hist0(dl, msg);
-
- /* set Vs to 0, because it is used as index when resending DISC */
- dl->v_send = 0;
-
- /* Set states */
- dl->own_busy = dl->peer_busy = 0;
- dl->retrans_ctr = 0;
- lapd_dl_newstate(dl, LAPD_STATE_DISC_SENT);
-
- /* Tramsmit and start T200 */
- dl->send_ph_data_req(&nctx, msg);
- lapd_start_t200(dl);
-
- return 0;
-}
-
-/* request release of link in idle state */
-static int lapd_rel_req_idle(struct osmo_dlsap_prim *dp,
- struct lapd_msg_ctx *lctx)
-{
- struct lapd_datalink *dl = lctx->dl;
- struct msgb *msg = dp->oph.msg;
-
- msgb_free(msg);
-
- /* send notification to L3 */
- return send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, &dl->lctx);
-}
-
-/* statefull handling for DL SAP messages from L3 */
-static const struct l2downstate {
- uint32_t states;
- int prim, op;
- const char *name;
- int (*rout) (struct osmo_dlsap_prim *dp,
- struct lapd_msg_ctx *lctx);
-} l2downstatelist[] = {
- /* create and send UI command */
- {ALL_STATES,
- PRIM_DL_UNIT_DATA, PRIM_OP_REQUEST,
- "DL-UNIT-DATA-REQUEST", lapd_udata_req},
-
- /* create and send SABM command */
- {SBIT(LAPD_STATE_IDLE),
- PRIM_DL_EST, PRIM_OP_REQUEST,
- "DL-ESTABLISH-REQUEST", lapd_est_req},
-
- /* create and send I command */
- {SBIT(LAPD_STATE_MF_EST) |
- SBIT(LAPD_STATE_TIMER_RECOV),
- PRIM_DL_DATA, PRIM_OP_REQUEST,
- "DL-DATA-REQUEST", lapd_data_req},
-
- /* suspend datalink */
- {SBIT(LAPD_STATE_MF_EST) |
- SBIT(LAPD_STATE_TIMER_RECOV),
- PRIM_DL_SUSP, PRIM_OP_REQUEST,
- "DL-SUSPEND-REQUEST", lapd_susp_req},
-
- /* create and send SABM command (resume) */
- {SBIT(LAPD_STATE_MF_EST) |
- SBIT(LAPD_STATE_TIMER_RECOV),
- PRIM_DL_RES, PRIM_OP_REQUEST,
- "DL-RESUME-REQUEST", lapd_res_req},
-
- /* create and send SABM command (reconnect) */
- {SBIT(LAPD_STATE_IDLE) |
- SBIT(LAPD_STATE_MF_EST) |
- SBIT(LAPD_STATE_TIMER_RECOV),
- PRIM_DL_RECON, PRIM_OP_REQUEST,
- "DL-RECONNECT-REQUEST", lapd_res_req},
-
- /* create and send DISC command */
- {SBIT(LAPD_STATE_SABM_SENT) |
- SBIT(LAPD_STATE_MF_EST) |
- SBIT(LAPD_STATE_TIMER_RECOV) |
- SBIT(LAPD_STATE_DISC_SENT),
- PRIM_DL_REL, PRIM_OP_REQUEST,
- "DL-RELEASE-REQUEST", lapd_rel_req},
-
- /* release in idle state */
- {SBIT(LAPD_STATE_IDLE),
- PRIM_DL_REL, PRIM_OP_REQUEST,
- "DL-RELEASE-REQUEST", lapd_rel_req_idle},
-};
-
-#define L2DOWNSLLEN \
- (sizeof(l2downstatelist) / sizeof(struct l2downstate))
-
-int lapd_recv_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
-{
- struct lapd_datalink *dl = lctx->dl;
- int i, supported = 0;
- struct msgb *msg = dp->oph.msg;
- int rc;
-
- /* find function for current state and message */
- for (i = 0; i < L2DOWNSLLEN; i++) {
- if (dp->oph.primitive == l2downstatelist[i].prim
- && dp->oph.operation == l2downstatelist[i].op) {
- supported = 1;
- if ((SBIT(dl->state) & l2downstatelist[i].states))
- break;
- }
- }
- if (!supported) {
- LOGDL(dl, LOGL_NOTICE, "Message %u/%u unsupported\n",
- dp->oph.primitive, dp->oph.operation);
- msgb_free(msg);
- return 0;
- }
- if (i == L2DOWNSLLEN) {
- LOGDL(dl, LOGL_NOTICE, "Message %u/%u unhandled at this state %s\n",
- dp->oph.primitive, dp->oph.operation, lapd_state_name(dl->state));
- msgb_free(msg);
- return 0;
- }
-
- LOGDL(dl, LOGL_INFO, "Message %s received in state %s\n",
- l2downstatelist[i].name, lapd_state_name(dl->state));
-
- rc = l2downstatelist[i].rout(dp, lctx);
-
- return rc;
-}
-
-/*! @} */