/* GSM LAPDm (TS 04.06) implementation */
-/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2010-2011 by Harald Welte <laforge@gnumonks.org>
* (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
*
* All Rights Reserved
#include <errno.h>
#include <arpa/inet.h>
-#include <osmocore/logging.h>
-#include <osmocore/timer.h>
-#include <osmocore/msgb.h>
-#include <osmocore/tlv.h>
-#include <osmocore/utils.h>
-#include <osmocore/rsl.h>
-#include <osmocore/protocol/gsm_04_08.h>
-#include <osmocore/protocol/gsm_08_58.h>
-
-#include <osmocom/bb/common/osmocom_data.h>
-#include <osmocom/bb/common/l1ctl.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/rsl.h>
+#include <osmocom/gsm/prim.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+
#include <osmocom/bb/common/lapdm.h>
#include <osmocom/bb/common/logging.h>
-#include <l1ctl_proto.h>
-
/* TS 04.06 Figure 4 / Section 3.2 */
#define LAPDm_LPD_NORMAL 0
#define LAPDm_LPD_SMSCB 1
};
static void lapdm_t200_cb(void *data);
-static int rslms_send_i(struct lapdm_msg_ctx *mctx);
+static int rslms_send_i(struct lapdm_msg_ctx *mctx, int line);
/* UTILITY FUNCTIONS */
dl->entity = entity;
}
-void lapdm_init(struct lapdm_entity *le, struct osmocom_ms *ms)
+void lapdm_entity_init(struct lapdm_entity *le)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(le->datalink); i++)
lapdm_dl_init(&le->datalink[i], le);
+}
- le->ms = ms;
+void lapdm_channel_init(struct lapdm_channel *lc)
+{
+ lapdm_entity_init(&lc->lapdm_acch);
+ lapdm_entity_init(&lc->lapdm_dcch);
}
+
static void lapdm_dl_flush_send(struct lapdm_datalink *dl)
{
struct msgb *msg;
dl->tx_length[i] = 0;
}
-void lapdm_exit(struct lapdm_entity *le)
+void lapdm_entity_exit(struct lapdm_entity *le)
{
unsigned int i;
struct lapdm_datalink *dl;
}
}
+void lapdm_channel_exit(struct lapdm_channel *lc)
+{
+ lapdm_entity_exit(&lc->lapdm_acch);
+ lapdm_entity_exit(&lc->lapdm_dcch);
+}
+
static void lapdm_dl_newstate(struct lapdm_datalink *dl, uint32_t state)
{
LOGP(DLAPDM, LOGL_INFO, "new state %s -> %s\n",
memset(data, 0x2B, pad_len);
}
+/* 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,
uint8_t chan_nr, uint8_t link_id, uint8_t n201)
{
struct lapdm_entity *le = dl->entity;
- struct osmocom_ms *ms = le->ms;
+ struct osmo_phsap_prim pp;
+
+ osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_RACH,
+ PRIM_OP_REQUEST, msg);
+ pp.u.data.chan_nr = chan_nr;
+ pp.u.data.link_id = link_id;
/* if there is a pending message, queue it */
if (le->tx_pending) {
/* send the frame now */
le->tx_pending = 0; /* disabled flow control */
lapdm_pad_msgb(msg, n201);
- return l1ctl_tx_data_req(ms, msg, chan_nr, link_id);
+
+ return le->l1_prim_cb(&pp.oph, le->l1_ctx);
}
/* get next frame from the tx queue. because the ms has multiple datalinks,
* each datalink's queue is read round-robin.
*/
-int l2_ph_data_conf(struct msgb *msg, struct lapdm_entity *le)
+static int l2_ph_data_conf(struct msgb *msg, struct lapdm_entity *le)
{
- struct osmocom_ms *ms = le->ms;
struct lapdm_datalink *dl;
int last = le->last_tx_dequeue;
int i = last, n = ARRAY_SIZE(le->datalink);
- uint8_t chan_nr, link_id, n201;
+ struct osmo_phsap_prim pp;
+ uint8_t n201;
/* we may send again */
le->tx_pending = 0;
/* free confirm message */
- msgb_free(msg);
+ if (msg)
+ msgb_free(msg);
/* round-robin dequeue */
do {
if (!msg)
return 0;
+ osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA,
+ PRIM_OP_REQUEST, msg);
+
/* Pull chan_nr and link_id */
- chan_nr = *msg->data;
+ pp.u.data.chan_nr = *msg->data;
msgb_pull(msg, 1);
- link_id = *msg->data;
+ pp.u.data.link_id = *msg->data;
msgb_pull(msg, 1);
n201 = *msg->data;
msgb_pull(msg, 1);
/* Pad the frame, we can transmit now */
le->tx_pending = 1;
lapdm_pad_msgb(msg, n201);
- return l1ctl_tx_data_req(ms, msg, chan_nr, link_id);
+
+ return le->l1_prim_cb(&pp.oph, le->l1_ctx);
}
/* Create RSLms various RSLms messages */
rsl_rll_push_l3(msg, msg_type, mctx->chan_nr, mctx->link_id, 1);
/* send off the RSLms message to L3 */
- return rslms_sendmsg(msg, mctx->dl->entity->ms);
+ return rslms_sendmsg(msg, mctx->dl->entity);
}
/* Take a B4 format message from L1 and create RSLms UNIT DATA IND */
mctx->link_id, 1);
rllh = (struct abis_rsl_rll_hdr *)msgb_l2(msg);
- rllh->data[0] = RSL_IE_ACCESS_DELAY;
+ rllh->data[0] = RSL_IE_TIMING_ADVANCE;
rllh->data[1] = mctx->ta_ind;
rllh->data[2] = RSL_IE_MS_POWER;
rllh->data[3] = mctx->tx_power_ind;
- return rslms_sendmsg(msg, mctx->dl->entity->ms);
+ return rslms_sendmsg(msg, mctx->dl->entity);
}
static int send_rll_simple(uint8_t msg_type, struct lapdm_msg_ctx *mctx)
msg = rsl_rll_simple(msg_type, mctx->chan_nr, mctx->link_id, 1);
/* send off the RSLms message to L3 */
- return rslms_sendmsg(msg, mctx->dl->entity->ms);
+ return rslms_sendmsg(msg, mctx->dl->entity);
}
static int rsl_rll_error(uint8_t cause, struct lapdm_msg_ctx *mctx)
{
struct msgb *msg;
- uint8_t *tlv;
LOGP(DLAPDM, LOGL_NOTICE, "sending MDL-ERROR-IND %d\n", cause);
msg = rsl_rll_simple(RSL_MT_ERROR_IND, mctx->chan_nr, mctx->link_id, 1);
- msg->l2h = msgb_put(msg, sizeof(struct abis_rsl_rll_hdr) + 3);
- tlv = msg->l2h + sizeof(struct abis_rsl_rll_hdr);
- tlv[0] = RSL_IE_RLM_CAUSE;
- tlv[1] = 1;
- tlv[2] = cause;
- return rslms_sendmsg(msg, mctx->dl->entity->ms);
+ msg->l2h = msgb_put(msg, sizeof(struct abis_rsl_rll_hdr));
+ msgb_tlv_put(msg, RSL_IE_RLM_CAUSE, 1, &cause);
+ return rslms_sendmsg(msg, mctx->dl->entity);
}
static int check_length_ind(struct lapdm_msg_ctx *mctx, uint8_t length_ind)
send_rll_simple(RSL_MT_REL_IND, &dl->mctx);
/* send MDL ERROR INIDCATION to L3 */
rsl_rll_error(RLL_CAUSE_T200_EXPIRED, &dl->mctx);
- /* flush buffers */
+ /* flush tx buffers */
lapdm_dl_flush_tx(dl);
- lapdm_dl_flush_send(dl);
/* go back to idle state */
lapdm_dl_newstate(dl, LAPDm_STATE_IDLE);
/* NOTE: we must not change any other states or buffers
/* increment re-transmission counter */
dl->retrans_ctr++;
/* restart T200 (PH-READY-TO-SEND) */
- bsc_schedule_timer(&dl->t200, T200);
+ osmo_timer_schedule(&dl->t200, T200);
break;
case LAPDm_STATE_DISC_SENT:
/* 5.4.4.3 */
/* increment re-transmission counter */
dl->retrans_ctr++;
/* restart T200 (PH-READY-TO-SEND) */
- bsc_schedule_timer(&dl->t200, T200);
+ osmo_timer_schedule(&dl->t200, T200);
break;
case LAPDm_STATE_MF_EST:
/* 5.5.7 */
dl->retrans_ctr++;
if (dl->retrans_ctr < N200) {
/* retransmit I frame (V_s-1) with P=1, if any */
- if (dl->tx_length[dl->V_send - 1]) {
+ if (dl->tx_length[sub_mod8(dl->V_send, 1)]) {
struct msgb *msg;
int length;
LOGP(DLAPDM, LOGL_INFO, "retransmit last frame "
- "V(S)=%d\n", dl->V_send - 1);
+ "V(S)=%d\n", sub_mod8(dl->V_send, 1));
/* Create I frame (segment) from tx_hist */
- length = dl->tx_length[dl->V_send - 1];
+ length = dl->tx_length[sub_mod8(dl->V_send, 1)];
msg = msgb_alloc_headroom(23+10, 10, "LAPDm I");
msg->l2h = msgb_put(msg, length);
- memcpy(msg->l2h, dl->tx_hist[dl->V_send - 1],
+ memcpy(msg->l2h,
+ dl->tx_hist[sub_mod8(dl->V_send, 1)],
length);
msg->l2h[1] = LAPDm_CTRL_I(dl->V_recv,
- dl->V_send - 1, 1); /* P=1 */
+ sub_mod8(dl->V_send, 1), 1); /* P=1 */
tx_ph_data_enqueue(dl, msg, dl->mctx.chan_nr,
dl->mctx.link_id, dl->mctx.n201);
} else {
}
}
/* restart T200 (PH-READY-TO-SEND) */
- bsc_schedule_timer(&dl->t200, T200);
+ osmo_timer_schedule(&dl->t200, T200);
} else {
/* send MDL ERROR INIDCATION to L3 */
rsl_rll_error(RLL_CAUSE_T200_EXPIRED, &dl->mctx);
|| (rej && nr == dl->V_ack)) {
LOGP(DLAPDM, LOGL_INFO, "reset t200\n");
t200_reset = 1;
- bsc_del_timer(&dl->t200);
+ osmo_timer_del(&dl->t200);
/* 5.5.3.1 Note 1 + 2 imply timer recovery cond. */
}
/* 5.7.4: N(R) sequence error
if (dl->tx_length[dl->V_send - 1]) {
LOGP(DLAPDM, LOGL_INFO, "start T200, due to unacked I "
"frame(s)\n");
- bsc_schedule_timer(&dl->t200, T200);
+ osmo_timer_schedule(&dl->t200, T200);
}
}
}
/* 5.4.6.2 send DM with F=P */
lapdm_send_dm(mctx);
/* reset Timer T200 */
- bsc_del_timer(&dl->t200);
+ osmo_timer_del(&dl->t200);
msgb_free(msg);
return send_rll_simple(RSL_MT_REL_CONF, mctx);
default:
break;
case LAPDm_STATE_DISC_SENT:
/* reset Timer T200 */
- bsc_del_timer(&dl->t200);
+ osmo_timer_del(&dl->t200);
/* go to idle state */
lapdm_dl_newstate(dl, LAPDm_STATE_IDLE);
rc = send_rll_simple(RSL_MT_REL_CONF, mctx);
return 0;
}
/* reset T200 */
- bsc_del_timer(&dl->t200);
+ osmo_timer_del(&dl->t200);
rc = send_rll_simple(RSL_MT_REL_IND, mctx);
msgb_free(msg);
break;
/* 5.4.6.2 send DM with F=P */
lapdm_send_dm(mctx);
/* reset Timer T200 */
- bsc_del_timer(&dl->t200);
+ osmo_timer_del(&dl->t200);
return send_rll_simple(RSL_MT_REL_IND, mctx);
case LAPDm_STATE_MF_EST:
case LAPDm_STATE_TIMER_RECOV:
/* send UA response */
lapdm_send_ua(mctx, length, msg->l2h + 3);
/* reset Timer T200 */
- bsc_del_timer(&dl->t200);
+ osmo_timer_del(&dl->t200);
/* enter idle state */
lapdm_dl_newstate(dl, LAPDm_STATE_IDLE);
/* send notification to L3 */
case LAPDm_STATE_DISC_SENT:
LOGP(DLAPDM, LOGL_INFO, "UA in disconnect state\n");
/* reset Timer T200 */
- bsc_del_timer(&dl->t200);
+ osmo_timer_del(&dl->t200);
/* go to idle state */
lapdm_dl_newstate(dl, LAPDm_STATE_IDLE);
rc = send_rll_simple(RSL_MT_REL_CONF, mctx);
}
LOGP(DLAPDM, LOGL_INFO, "UA in SABM state\n");
/* reset Timer T200 */
- bsc_del_timer(&dl->t200);
+ osmo_timer_del(&dl->t200);
/* compare UA with SABME if contention resolution is applied */
if (dl->tx_hist[0][2] >> 2) {
rc = check_length_ind(mctx, msg->l2h[2]);
/* enter multiple-frame-established state */
lapdm_dl_newstate(dl, LAPDm_STATE_MF_EST);
/* send outstanding frames, if any (resume / reconnect) */
- rslms_send_i(mctx);
+ rslms_send_i(mctx, __LINE__);
/* send notification to L3 */
rc = send_rll_simple(RSL_MT_EST_CONF, mctx);
msgb_free(msg);
rsl_rll_error(RLL_CAUSE_SFRM_INC_PARAM, mctx);
return -EIO;
}
- /* 5.4.2.2: Inidcate error on supervisory reponse F=1 */
+
if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP
- && LAPDm_CTRL_PF_BIT(mctx->ctrl)) {
+ && LAPDm_CTRL_PF_BIT(mctx->ctrl)
+ && dl->state != LAPDm_STATE_TIMER_RECOV) {
+ /* 5.4.2.2: Inidcate error on supervisory reponse F=1 */
LOGP(DLAPDM, LOGL_NOTICE, "S frame response with F=1 error\n");
rsl_rll_error(RLL_CAUSE_UNSOL_SPRV_RESP, mctx);
}
"so we reply with RR frame\n");
lapdm_send_rnr(mctx, 1);
}
+ } else if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP
+ && LAPDm_CTRL_PF_BIT(mctx->ctrl)
+ && dl->state == LAPDm_STATE_TIMER_RECOV) {
+ LOGP(DLAPDM, 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 = LAPDm_CTRL_Nr(mctx->ctrl);
+ /* reset Timer T200 */
+ osmo_timer_del(&dl->t200);
+ /* 5.5.7 Clear timer recovery condition */
+ lapdm_dl_newstate(dl, LAPDm_STATE_MF_EST);
}
/* Send message, if possible due to acknowledged data */
- rslms_send_i(mctx);
+ rslms_send_i(mctx, __LINE__);
break;
case LAPDm_S_RNR:
LOGP(DLAPDM, LOGL_INFO, "RNR poll response "
"and we in timer recovery state, so "
"we leave that state\n");
- /* Clear timer recovery condition */
+ /* 5.5.7 Clear timer recovery condition */
lapdm_dl_newstate(dl, LAPDm_STATE_MF_EST);
/* V(S) to the N(R) in the RNR frame */
dl->V_send = LAPDm_CTRL_Nr(mctx->ctrl);
"received\n");
/* Send message, if possible due to acknowledged data */
- rslms_send_i(mctx);
+ rslms_send_i(mctx, __LINE__);
break;
case LAPDm_S_REJ:
/* V(S) and V(A) to the N(R) in the REJ frame */
dl->V_send = dl->V_ack = LAPDm_CTRL_Nr(mctx->ctrl);
/* reset Timer T200 */
- bsc_del_timer(&dl->t200);
+ osmo_timer_del(&dl->t200);
/* 5.5.3.2 */
if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_CMD
&& LAPDm_CTRL_PF_BIT(mctx->ctrl)) {
"recovery state received\n");
/* Clear an existing peer receiver busy condition */
dl->peer_busy = 0;
- /* Clear timer recovery condition */
+ /* 5.5.7 Clear timer recovery condition */
lapdm_dl_newstate(dl, LAPDm_STATE_MF_EST);
/* V(S) and V(A) to the N(R) in the REJ frame */
dl->V_send = dl->V_ack = LAPDm_CTRL_Nr(mctx->ctrl);
/* reset Timer T200 */
- bsc_del_timer(&dl->t200);
+ osmo_timer_del(&dl->t200);
} else {
/* Clear an existing peer receiver busy condition */
dl->peer_busy = 0;
/* FIXME: 5.5.4.2 2) */
/* Send message, if possible due to acknowledged data */
- rslms_send_i(mctx);
+ rslms_send_i(mctx, __LINE__);
break;
default:
/* send a DATA INDICATION to L3 */
msg->l3h = msg->l2h + 3;
msgb_pull_l2h(msg);
+ msg->len = length;
+ msg->tail = msg->data + length;
rc = send_rslms_rll_l3(RSL_MT_DATA_IND, mctx, msg);
} else {
/* create rcv_buffer */
/* if the last segment was received */
if (!(msg->l2h[2] & LAPDm_MORE)) {
LOGP(DLAPDM, LOGL_INFO, "message in multiple I "
- "frames (next message)\n");
+ "frames (last message)\n");
rc = send_rslms_rll_l3(RSL_MT_DATA_IND, mctx,
dl->rcv_buffer);
dl->rcv_buffer = NULL;
} else
LOGP(DLAPDM, LOGL_INFO, "message in multiple I "
- "frames (last message)\n");
+ "frames (next message)\n");
msgb_free(msg);
}
/* check if we are not in own receiver busy */
if (!dl->own_busy) {
/* NOTE: V(R) is already set above */
- rc = rslms_send_i(mctx);
+ rc = rslms_send_i(mctx, __LINE__);
if (rc) {
LOGP(DLAPDM, LOGL_INFO, "we are not busy and "
"have no pending data, send RR\n");
/* Send RR with F=0 */
return lapdm_send_rr(mctx, 0);
}
+ /* all I or one RR is sent, we are done */
+ return 0;
} else {
LOGP(DLAPDM, LOGL_INFO, "we are busy, send RNR\n");
/* Send RNR with F=0 */
}
/* Send message, if possible due to acknowledged data */
- rslms_send_i(mctx);
+ rslms_send_i(mctx, __LINE__);
return rc;
}
}
/* input into layer2 (from layer 1) */
-int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le, struct l1ctl_info_dl *l1i)
+static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le, uint8_t chan_nr, uint8_t link_id)
{
- uint8_t cbits = l1i->chan_nr >> 3;
- uint8_t sapi = l1i->link_id & 7;
+ uint8_t cbits = chan_nr >> 3;
+ uint8_t sapi = link_id & 7;
struct lapdm_msg_ctx mctx;
int rc = 0;
* 23byte mac block. The l1h has already been purged. */
mctx.dl = datalink_for_sapi(le, sapi);
- mctx.chan_nr = l1i->chan_nr;
- mctx.link_id = l1i->link_id;
+ mctx.chan_nr = chan_nr;
+ mctx.link_id = link_id;
mctx.addr = mctx.ctrl = 0;
/* G.2.1 No action schall be taken on frames containing an unallocated
return rc;
}
+static int l2_ph_chan_conf(struct msgb *msg, struct lapdm_entity *le, uint32_t frame_nr);
+
+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)
+ return -ENODEV;
+
+ if (oph->operation != PRIM_OP_INDICATION)
+ return -ENODEV;
+
+ switch (oph->primitive) {
+ case PRIM_PH_DATA:
+ if (oph->operation != PRIM_OP_INDICATION)
+ return -ENODEV;
+ rc = l2_ph_data_ind(oph->msg, le, pp->u.data.chan_nr,
+ pp->u.data.link_id);
+ break;
+ case PRIM_PH_RTS:
+ if (oph->operation != PRIM_OP_INDICATION)
+ return -ENODEV;
+ rc = l2_ph_data_conf(oph->msg, le);
+ break;
+ case PRIM_PH_RACH:
+ switch (oph->operation) {
+ case PRIM_OP_INDICATION:
+#warning FIX BTS
+ //rc = l2_ph_rach_ind(le, pp->u.rach_ind.ra, pp->u.rach_ind.fn);
+ break;
+ case PRIM_OP_CONFIRM:
+ rc = l2_ph_chan_conf(oph->msg, le, pp->u.rach_ind.fn);
+ break;
+ default:
+ return -EIO;
+ }
+ break;
+ }
+
+ return rc;
+}
+
+
/* L3 -> L2 / RSLMS -> LAPDm */
/* L3 requests establishment of data link */
* command shall contain the layer 3 message unit */
length = TLVP_LEN(&tv, RSL_IE_L3_INFO);
LOGP(DLAPDM, LOGL_INFO, "perform establishment with content "
- "(SAMB)\n");
+ "(SABM)\n");
} else {
/* normal establishment procedure */
length = 0;
- LOGP(DLAPDM, LOGL_INFO, "perform normal establishm. (SAMB)\n");
+ LOGP(DLAPDM, LOGL_INFO, "perform normal establishm. (SABM)\n");
}
/* check if the layer3 message length exceeds N201 */
/* Transmit-buffer carries exactly one segment */
memcpy(dl->tx_hist[0], msg->l2h, 3 + length);
dl->tx_length[0] = 3 + length;
- /* set Vs to 0, because it is used as index when resending SAMB */
+ /* set Vs to 0, because it is used as index when resending SABM */
dl->V_send = 0;
/* Set states */
lapdm_dl_newstate(dl, LAPDm_STATE_SABM_SENT);
/* Tramsmit and start T200 */
- bsc_schedule_timer(&dl->t200, T200);
+ osmo_timer_schedule(&dl->t200, T200);
return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, n201);
}
struct tlv_parsed tv;
int length;
uint8_t n201 = 23; //FIXME
+ uint8_t ta = 0, tx_power = 0;
/* 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)) {
+ ta = *TLVP_VAL(&tv, RSL_IE_TIMING_ADVANCE);
+ }
+ if (TLVP_PRESENT(&tv, RSL_IE_MS_POWER)) {
+ tx_power = *TLVP_VAL(&tv, RSL_IE_MS_POWER);
+ }
+ if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
+ LOGP(DLAPDM, LOGL_ERROR, "unit data request without message "
+ "error\n");
+ msgb_free(msg);
+ return -EINVAL;
+ }
length = TLVP_LEN(&tv, RSL_IE_L3_INFO);
/* check if the layer3 message length exceeds N201 */
- if (length + 3 > 18) { /* FIXME: do we know the channel N201? */
+ if (length + 5 > 23) { /* FIXME: do we know the channel N201? */
LOGP(DLAPDM, LOGL_ERROR, "frame too large: %d > N201(%d) "
- "(discarding)\n", length + 3, 18);
+ "(discarding)\n", length + 5, 23);
msgb_free(msg);
return -EIO;
}
- LOGP(DLAPDM, LOGL_INFO, "sending unit data\n");
+ LOGP(DLAPDM, LOGL_INFO, "sending unit data (tx_power=%d, ta=%d)\n",
+ tx_power, ta);
/* Remove RLL header from msgb */
msgb_pull_l2h(msg);
- /* Push LAPDm header on msgb */
- msg->l2h = msgb_push(msg, 3);
- msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_CMD);
- msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_UI, 0);
- msg->l2h[2] = LAPDm_LEN(length);
+ /* Push L1 + LAPDm header on msgb */
+ msg->l2h = msgb_push(msg, 2 + 3);
+ msg->l2h[0] = tx_power;
+ msg->l2h[1] = ta;
+ msg->l2h[2] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_CMD);
+ msg->l2h[3] = LAPDm_CTRL_U(LAPDm_U_UI, 0);
+ msg->l2h[4] = LAPDm_LEN(length);
// FIXME: short L2 header support
/* Tramsmit */
msgb_enqueue(&dl->send_queue, msg);
/* Send message, if possible */
- rslms_send_i(&dl->mctx);
+ rslms_send_i(&dl->mctx, __LINE__);
return 0;
}
/* Send next I frame from queued/buffered data */
-static int rslms_send_i(struct lapdm_msg_ctx *mctx)
+static int rslms_send_i(struct lapdm_msg_ctx *mctx, int line)
{
struct lapdm_datalink *dl = mctx->dl;
uint8_t chan_nr = mctx->chan_nr;
int k = k_sapi[sapi];
struct msgb *msg;
int length, left;
- int rc = -1; /* we sent nothing */
+ int rc = - 1; /* we sent nothing */
+
+ LOGP(DLAPDM, LOGL_INFO, "%s() called from line %d\n", __func__, line);
next_frame:
length = left;
if (length > mctx->n201 - 3)
length = mctx->n201 - 3;
-printf("msg-len %d sent %d left %d N201 %d length %d first byte %02x\n", msgb_l3len(dl->send_buffer), dl->send_out, left, mctx->n201, length, dl->send_buffer->l3h[0]);
+ LOGP(DLAPDM, 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,
+ mctx->n201, length, dl->send_buffer->l3h[0]);
/* If message in send-buffer is completely sent */
if (left == 0) {
msgb_free(dl->send_buffer);
/* 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 (!bsc_timer_pending(&dl->t200))
- bsc_schedule_timer(&dl->t200, T200);
+ if (!osmo_timer_pending(&dl->t200))
+ osmo_timer_schedule(&dl->t200, T200);
tx_ph_data_enqueue(dl, msg, chan_nr, link_id, mctx->n201);
/* put back the send-buffer to the send-queue (first position) */
if (dl->send_buffer) {
+ LOGP(DLAPDM, LOGL_INFO, "put frame in sendbuffer back to "
+ "queue\n");
llist_add(&dl->send_buffer->list, &dl->send_queue);
dl->send_buffer = NULL;
- }
+ } else
+ LOGP(DLAPDM, LOGL_INFO, "no frame in sendbuffer\n");
- /* Clear transmit and send buffer, if any */
+ /* Clear transmit buffer, but keep send buffer */
lapdm_dl_flush_tx(dl);
- lapdm_dl_flush_send(dl);
msgb_free(msg);
}
length = TLVP_LEN(&tv, RSL_IE_L3_INFO);
- LOGP(DLAPDM, LOGL_INFO, "perform re-establishment (SAMB)\n");
+ LOGP(DLAPDM, LOGL_INFO, "perform re-establishment (SABM) length=%d\n",
+ length);
/* Replace message in the send-buffer (reconnect) */
if (dl->send_buffer)
msgb_free(dl->send_buffer);
dl->send_out = 0;
- dl->send_buffer = msg;
+ if (length) {
+ /* Remove the RSL/RLL header */
+ msgb_pull_l2h(msg);
+ /* Write data into the send buffer, to be sent first */
+ dl->send_buffer = msg;
+ }
/* Discard partly received L3 message */
if (dl->rcv_buffer) {
/* Transmit-buffer carries exactly one segment */
memcpy(dl->tx_hist[0], msg->l2h, 3);
dl->tx_length[0] = 3;
- /* set Vs to 0, because it is used as index when resending SAMB */
+ /* set Vs to 0, because it is used as index when resending SABM */
dl->V_send = 0;
/* Set states */
lapdm_dl_newstate(dl, LAPDm_STATE_SABM_SENT);
/* Tramsmit and start T200 */
- bsc_schedule_timer(&dl->t200, T200);
+ osmo_timer_schedule(&dl->t200, T200);
return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, n201);
}
LOGP(DLAPDM, LOGL_INFO, "perform local release\n");
msgb_free(msg);
/* reset Timer T200 */
- bsc_del_timer(&dl->t200);
+ osmo_timer_del(&dl->t200);
/* enter idle state */
lapdm_dl_newstate(dl, LAPDm_STATE_IDLE);
/* flush buffers */
lapdm_dl_newstate(dl, LAPDm_STATE_DISC_SENT);
/* Tramsmit and start T200 */
- bsc_schedule_timer(&dl->t200, T200);
+ osmo_timer_schedule(&dl->t200, T200);
return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, dl->mctx.n201);
}
}
/* L3 requests channel in idle state */
-static int rslms_rx_chan_rqd(struct osmocom_ms *ms, struct msgb *msg)
+static int rslms_rx_chan_rqd(struct lapdm_channel *lc, struct msgb *msg)
{
struct abis_rsl_cchan_hdr *cch = msgb_l2(msg);
- int rc;
+ 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) {
LOGP(DRSL, LOGL_ERROR, "Message too short for CHAN RQD!\n");
LOGP(DRSL, LOGL_ERROR, "Missing REQ REFERENCE IE\n");
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) {
LOGP(DRSL, LOGL_ERROR, "Missing ACCESS_DELAY IE\n");
return -EINVAL;
}
+ /* TA = 0 - delay */
+ pp.u.rach_req.ta = 0 - cch->data[5];
+
if (cch->data[6] != RSL_IE_MS_POWER) {
LOGP(DRSL, LOGL_ERROR, "Missing MS POWER IE\n");
return -EINVAL;
}
-
- /* TA = 0 - delay */
- rc = l1ctl_tx_param_req(ms, 0 - cch->data[5], cch->data[7]);
-
- rc = l1ctl_tx_rach_req(ms, cch->data[1], cch->data[2], cch->data[3]);
+ pp.u.rach_req.tx_power = cch->data[7];
msgb_free(msg);
- return rc;
+ return lc->lapdm_dcch.l1_prim_cb(&pp.oph, l1ctx);
}
/* L1 confirms channel request */
-int l2_ph_chan_conf(struct msgb *msg, struct osmocom_ms *ms,
- struct l1ctl_info_dl *dl)
+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, htonl(dl->frame_nr));
+ gsm_fn2gsmtime(&tm, frame_nr);
msgb_pull_l2h(msg);
msg->l2h = msgb_push(msg, sizeof(*ch) + sizeof(*ref));
ref = (struct gsm48_req_ref *) (ch->data + 1);
ref->t1 = tm.t1;
ref->t2 = tm.t2;
- ref->t3_low = tm.t3 & 0x3;
+ ref->t3_low = tm.t3 & 0x7;
ref->t3_high = tm.t3 >> 3;
- return rslms_sendmsg(msg, ms);
-}
-
-/* Names for Radio Link Layer Management */
-static const struct value_string rsl_msg_names[] = {
- { RSL_MT_DATA_REQ, "RSL_MT_DATA_REQ" },
- { RSL_MT_DATA_IND, "RSL_MT_DATA_IND" },
- { RSL_MT_ERROR_IND, "RSL_MT_ERROR_IND" },
- { RSL_MT_EST_REQ, "RSL_MT_EST_REQ" },
- { RSL_MT_EST_CONF, "RSL_MT_EST_CONF" },
- { RSL_MT_EST_IND, "RSL_MT_EST_IND" },
- { RSL_MT_EST_IND, "RSL_MT_REL_REQ" },
- { RSL_MT_REL_REQ, "RSL_MT_REL_REQ" },
- { RSL_MT_REL_CONF, "RSL_MT_REL_CONF" },
- { RSL_MT_REL_IND, "RSL_MT_REL_IND" },
- { RSL_MT_UNIT_DATA_REQ, "RSL_MT_UNIT_DATA_REQ" },
- { RSL_MT_UNIT_DATA_IND, "RSL_MT_UNIT_DATA_IND" },
- { RSL_MT_SUSP_REQ, "RSL_MT_SUSP_REQ" },
- { RSL_MT_SUSP_CONF, "RSL_MT_SUSP_CONF" },
- { RSL_MT_RES_REQ, "RSL_MT_RES_REQ" },
- { RSL_MT_RECON_REQ, "RSL_MT_RECON_REQ" },
- { RSL_MT_CHAN_RQD, "RSL_MT_CHAN_RQD" },
- { RSL_MT_CHAN_CONF, "RSL_MT_CHAN_CONF" },
- { 0, NULL }
-};
-
-const char *get_rsl_name(int value)
-{
- return get_value_string(rsl_msg_names, value);
+ return rslms_sendmsg(msg, le);
}
const char *lapdm_state_names[] = {
RSL_MT_RES_REQ, rslms_rx_rll_res_req},
/* create and send SABM command (reconnect) */
- {SBIT(LAPDm_STATE_MF_EST) |
+ {SBIT(LAPDm_STATE_IDLE) |
+ SBIT(LAPDm_STATE_MF_EST) |
SBIT(LAPDm_STATE_TIMER_RECOV),
RSL_MT_RECON_REQ, rslms_rx_rll_res_req},
(sizeof(l2downstatelist) / sizeof(struct l2downstate))
/* incoming RSLms RLL message from L3 */
-static int rslms_rx_rll(struct msgb *msg, struct osmocom_ms *ms)
+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;
}
if (rllh->link_id & 0x40)
- le = &ms->l2_entity.lapdm_acch;
+ le = &lc->lapdm_acch;
else
- le = &ms->l2_entity.lapdm_dcch;
+ le = &lc->lapdm_dcch;
/* G.2.1 No action schall be taken on frames containing an unallocated
* SAPI.
return -EINVAL;
}
- LOGP(DRSL, LOGL_INFO, "(ms %s) RLL Message '%s' received in state %s\n",
- ms->name, get_rsl_name(msg_type), lapdm_state_names[dl->state]);
+ LOGP(DRSL, LOGL_INFO, "(%p) RLL Message '%s' received in state %s\n",
+ lc->name, rsl_msg_name(msg_type), lapdm_state_names[dl->state]);
/* find function for current state and message */
for (i = 0; i < L2DOWNSLLEN; i++) {
}
/* incoming RSLms COMMON CHANNEL message from L3 */
-static int rslms_rx_com_chan(struct msgb *msg, struct osmocom_ms *ms)
+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;
switch (msg_type) {
case RSL_MT_CHAN_RQD:
/* create and send RACH request */
- rc = rslms_rx_chan_rqd(ms, msg);
+ rc = rslms_rx_chan_rqd(lc, msg);
break;
default:
LOGP(DRSL, LOGL_NOTICE, "Unknown COMMON CHANNEL msg %d!\n",
}
/* input into layer2 (from layer 3) */
-int rslms_recvmsg(struct msgb *msg, struct osmocom_ms *ms)
+int lapdm_rslms_recvmsg(struct msgb *msg, struct lapdm_channel *lc)
{
struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
int rc = 0;
switch (rslh->msg_discr & 0xfe) {
case ABIS_RSL_MDISC_RLL:
- rc = rslms_rx_rll(msg, ms);
+ rc = rslms_rx_rll(msg, lc);
break;
case ABIS_RSL_MDISC_COM_CHAN:
- rc = rslms_rx_com_chan(msg, ms);
+ rc = rslms_rx_com_chan(msg, lc);
break;
default:
LOGP(DRSL, LOGL_ERROR, "unknown RSLms message "
return rc;
}
-/* input function that L2 calls when sending messages up to L3 */
-int rslms_sendmsg(struct msgb *msg, struct osmocom_ms *ms)
+void lapdm_channel_set_l1(struct lapdm_channel *lc, osmo_prim_cb cb, void *ctx)
{
- if (!ms->l2_entity.msg_handler) {
- msgb_free(msg);
- return -EIO;
- }
-
- /* call the layer2 message handler that is registered */
- return ms->l2_entity.msg_handler(msg, ms);
+ 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;
}
-/* register message handler for messages that are sent from L2->L3 */
-int osmol2_register_handler(struct osmocom_ms *ms, osmol2_cb_t cb)
+void lapdm_channel_set_l3(struct lapdm_channel *lc, lapdm_cb_t cb, void *ctx)
{
- ms->l2_entity.msg_handler = cb;
-
- return 0;
+ lc->lapdm_dcch.l3_cb = cb;
+ lc->lapdm_acch.l3_cb = cb;
+ lc->lapdm_dcch.l3_ctx = ctx;
+ lc->lapdm_acch.l3_ctx = ctx;
}