diff options
Diffstat (limited to 'src/host/layer23/src/common/l1ctl.c')
-rw-r--r-- | src/host/layer23/src/common/l1ctl.c | 370 |
1 files changed, 314 insertions, 56 deletions
diff --git a/src/host/layer23/src/common/l1ctl.c b/src/host/layer23/src/common/l1ctl.c index 5d6d9c0c..94979103 100644 --- a/src/host/layer23/src/common/l1ctl.c +++ b/src/host/layer23/src/common/l1ctl.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> @@ -41,14 +37,63 @@ #include <osmocom/gsm/protocol/gsm_04_08.h> #include <osmocom/gsm/protocol/gsm_08_58.h> #include <osmocom/gsm/rsl.h> +#include <osmocom/gsm/lapdm.h> +#include <osmocom/gsm/gsm0502.h> #include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/l23_app.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/l1l2_interface.h> -#include <osmocom/gsm/lapdm.h> #include <osmocom/bb/common/logging.h> -extern struct gsmtap_inst *gsmtap_inst; +/* determine the CCCH block number based on the frame number */ +static unsigned int fn2ccch_block(uint32_t fn) +{ + int rc = gsm0502_fn2ccch_block(fn); + /* if FN is negative, we were called for something that's not CCCH! */ + OSMO_ASSERT(rc >= 0); + return rc; +} + +static uint8_t chantype_rsl2gsmtap_ext(uint8_t rsl_chantype, uint8_t link_id, uint32_t fn, uint8_t num_agch) +{ + uint8_t ret = chantype_rsl2gsmtap2(rsl_chantype, link_id, false); + if (ret != GSMTAP_CHANNEL_PCH) + return ret; + + if (fn2ccch_block(fn) >= num_agch) + return GSMTAP_CHANNEL_PCH; + return GSMTAP_CHANNEL_AGCH; +} + +static const uint8_t fill_frame[GSM_MACBLOCK_LEN] = { + 0x03, 0x03, 0x01, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, + 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, + 0x2B, 0x2B, 0x2B +}; + +/* Paging Request 1 with "no identity" content, i.e. empty/dummy paging */ +static const uint8_t paging_fill[GSM_MACBLOCK_LEN] = { + 0x15, 0x06, 0x21, 0x00, 0x01, 0xf0, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b }; + +static bool is_fill_frame(uint8_t chan_type, const uint8_t *data) +{ + switch (chan_type) { + case GSMTAP_CHANNEL_AGCH: + if (!memcmp(data, fill_frame, GSM_MACBLOCK_LEN)) + return true; + break; + case GSMTAP_CHANNEL_PCH: + if (!memcmp(data, paging_fill, GSM_MACBLOCK_LEN)) + return true; + break; + /* don't use 'default' case here as the above only conditionally return true */ + } + return false; +} static struct msgb *osmo_l1_alloc(uint8_t msg_type) { @@ -63,7 +108,7 @@ static struct msgb *osmo_l1_alloc(uint8_t msg_type) msg->l1h = msgb_put(msg, sizeof(*l1h)); l1h = (struct l1ctl_hdr *) msg->l1h; l1h->msg_type = msg_type; - + return msg; } @@ -145,6 +190,7 @@ static int rx_ph_data_ind(struct osmocom_ms *ms, struct msgb *msg) struct rx_meas_stat *meas = &ms->meas; uint8_t chan_type, chan_ts, chan_ss; uint8_t gsmtap_chan_type; + uint8_t bs_ag_blks_res; struct gsm_time tm; if (msgb_l1len(msg) < sizeof(*dl)) { @@ -159,7 +205,13 @@ static int rx_ph_data_ind(struct osmocom_ms *ms, struct msgb *msg) ccch = (struct l1ctl_data_ind *) msg->l2h; gsm_fn2gsmtime(&tm, ntohl(dl->frame_nr)); - rsl_dec_chan_nr(dl->chan_nr, &chan_type, &chan_ss, &chan_ts); + if (rsl_dec_chan_nr(dl->chan_nr, &chan_type, &chan_ss, &chan_ts) != 0) { + LOGP(DL1C, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, dl->chan_nr); + return -EINVAL; + } + DEBUGP(DL1C, "%s (%.4u/%.2u/%.2u) %d dBm: %s\n", rsl_chan_nr_str(dl->chan_nr), tm.t1, tm.t2, tm.t3, (int)dl->rx_level-110, @@ -229,11 +281,31 @@ static int rx_ph_data_ind(struct osmocom_ms *ms, struct msgb *msg) return 0; } - /* send CCCH data via GSMTAP */ - gsmtap_chan_type = chantype_rsl2gsmtap(chan_type, dl->link_id); - gsmtap_send(gsmtap_inst, ntohs(dl->band_arfcn), chan_ts, - gsmtap_chan_type, chan_ss, tm.fn, dl->rx_level-110, - dl->snr, ccch->data, sizeof(ccch->data)); + /* May not be initialized in some applications (e.g. ccch_scan) */ + if (ms->cellsel.si != NULL) + bs_ag_blks_res = ms->cellsel.si->bs_ag_blks_res; + else /* fall-back to 1 (this is what OsmoBTS does) */ + bs_ag_blks_res = 1; + + gsmtap_chan_type = chantype_rsl2gsmtap_ext(chan_type, dl->link_id, tm.fn, bs_ag_blks_res); + /* don't log fill frames via GSMTAP; they serve no purpose other than + * to clog up your logs */ + if (!is_fill_frame(gsmtap_chan_type, ccch->data)) { + /* send CCCH data via GSMTAP */ + gsmtap_send(l23_cfg.gsmtap.inst, ntohs(dl->band_arfcn), chan_ts, + gsmtap_chan_type, chan_ss, tm.fn, dl->rx_level-110, + dl->snr, ccch->data, sizeof(ccch->data)); + } + + /* Do not pass PDCH and CBCH frames to LAPDm */ + switch (chan_type) { + case RSL_CHAN_OSMO_PDCH: + case RSL_CHAN_OSMO_CBCH4: + case RSL_CHAN_OSMO_CBCH8: + /* TODO: pass directly to l23 application */ + msgb_free(msg); + return 0; + } /* determine LAPDm entity based on SACCH or not */ if (dl->link_id & 0x40) @@ -245,6 +317,7 @@ static int rx_ph_data_ind(struct osmocom_ms *ms, struct msgb *msg) PRIM_OP_INDICATION, msg); pp.u.data.chan_nr = dl->chan_nr; pp.u.data.link_id = dl->link_id; + pp.u.data.fn = tm.fn; /* send it up into LAPDm */ return lapdm_phsap_up(&pp.oph, le); @@ -284,7 +357,6 @@ int l1ctl_tx_data_req(struct osmocom_ms *ms, struct msgb *msg, struct l1ctl_hdr *l1h; struct l1ctl_info_ul *l1i_ul; uint8_t chan_type, chan_ts, chan_ss; - uint8_t gsmtap_chan_type; DEBUGP(DL1C, "(%s)\n", osmo_hexdump(msg->l2h, msgb_l2len(msg))); @@ -296,10 +368,16 @@ int l1ctl_tx_data_req(struct osmocom_ms *ms, struct msgb *msg, } /* send copy via GSMTAP */ - rsl_dec_chan_nr(chan_nr, &chan_type, &chan_ss, &chan_ts); - gsmtap_chan_type = chantype_rsl2gsmtap(chan_type, link_id); - gsmtap_send(gsmtap_inst, 0|0x4000, chan_ts, gsmtap_chan_type, - chan_ss, 0, 127, 255, msg->l2h, msgb_l2len(msg)); + if (rsl_dec_chan_nr(chan_nr, &chan_type, &chan_ss, &chan_ts) == 0) { + uint8_t gsmtap_chan_type = chantype_rsl2gsmtap2(chan_type, link_id, false); + gsmtap_send(l23_cfg.gsmtap.inst, ms->rrlayer.cd_now.arfcn | GSMTAP_ARFCN_F_UPLINK, + chan_ts, gsmtap_chan_type, chan_ss, 0, 127, 0, + msg->l2h, msgb_l2len(msg)); + } else { + LOGP(DL1C, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, chan_nr); + } /* prepend uplink info header */ l1i_ul = (struct l1ctl_info_ul *) msgb_push(msg, sizeof(*l1i_ul)); @@ -365,8 +443,8 @@ int l1ctl_tx_ccch_mode_req(struct osmocom_ms *ms, uint8_t ccch_mode) } /* Transmit L1CTL_TCH_MODE_REQ */ -int l1ctl_tx_tch_mode_req(struct osmocom_ms *ms, uint8_t tch_mode, - uint8_t audio_mode) +int l1ctl_tx_tch_mode_req(struct osmocom_ms *ms, uint8_t tch_mode, uint8_t audio_mode, uint8_t tch_flags, + uint8_t tch_loop_mode) { struct msgb *msg; struct l1ctl_tch_mode_req *req; @@ -380,6 +458,9 @@ int l1ctl_tx_tch_mode_req(struct osmocom_ms *ms, uint8_t tch_mode, req = (struct l1ctl_tch_mode_req *) msgb_put(msg, sizeof(*req)); req->tch_mode = tch_mode; req->audio_mode = audio_mode; + req->tch_flags = tch_flags; + req->tch_loop_mode = tch_loop_mode; + /* TODO: Set AMR codec in req if req->tch_mode==GSM48_CMODE_SPEECH_AMR */ return osmo_send_l1(ms, msg); } @@ -431,8 +512,9 @@ int l1ctl_tx_crypto_req(struct osmocom_ms *ms, uint8_t chan_nr, } /* Transmit L1CTL_RACH_REQ */ -int l1ctl_tx_rach_req(struct osmocom_ms *ms, uint8_t ra, uint16_t offset, - uint8_t combined) +int l1ctl_tx_rach_req(struct osmocom_ms *ms, + uint8_t chan_nr, uint8_t link_id, + uint8_t ra, uint16_t offset, uint8_t combined, uint8_t uic) { struct msgb *msg; struct l1ctl_info_ul *ul; @@ -442,20 +524,22 @@ int l1ctl_tx_rach_req(struct osmocom_ms *ms, uint8_t ra, uint16_t offset, if (!msg) return -1; - DEBUGP(DL1C, "RACH Req. offset=%d combined=%d\n", offset, combined); + DEBUGP(DL1C, "RACH Req. offset=%d combined=%d uic=0x%02x\n", offset, combined, uic); ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul)); + ul->chan_nr = chan_nr; + ul->link_id = link_id; req = (struct l1ctl_rach_req *) msgb_put(msg, sizeof(*req)); req->ra = ra; req->offset = htons(offset); req->combined = combined; + req->uic = uic; return osmo_send_l1(ms, msg); } /* Transmit L1CTL_DM_EST_REQ */ -int l1ctl_tx_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn, - uint8_t chan_nr, uint8_t tsc, uint8_t tch_mode, - uint8_t audio_mode) +int l1ctl_tx_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn, uint8_t chan_nr, uint8_t tsc, uint8_t tch_mode, + uint8_t audio_mode, uint8_t tch_flags) { struct msgb *msg; struct l1ctl_info_ul *ul; @@ -478,14 +562,13 @@ int l1ctl_tx_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn, req->h0.band_arfcn = htons(band_arfcn); req->tch_mode = tch_mode; req->audio_mode = audio_mode; + req->tch_flags = tch_flags; return osmo_send_l1(ms, msg); } -int l1ctl_tx_dm_est_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn, - uint16_t *ma, uint8_t ma_len, - uint8_t chan_nr, uint8_t tsc, uint8_t tch_mode, - uint8_t audio_mode) +int l1ctl_tx_dm_est_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn, uint16_t *ma, uint8_t ma_len, + uint8_t chan_nr, uint8_t tsc, uint8_t tch_mode, uint8_t audio_mode, uint8_t tch_flags) { struct msgb *msg; struct l1ctl_info_ul *ul; @@ -513,6 +596,7 @@ int l1ctl_tx_dm_est_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn, req->h1.ma[i] = htons(ma[i]); req->tch_mode = tch_mode; req->audio_mode = audio_mode; + req->tch_flags = tch_flags; return osmo_send_l1(ms, msg); } @@ -630,13 +714,10 @@ int l1ctl_tx_sim_req(struct osmocom_ms *ms, uint8_t *data, uint16_t length) /* just forward the SIM response to the SIM handler */ static int rx_l1_sim_conf(struct osmocom_ms *ms, struct msgb *msg) { - uint16_t len = msgb_l2len(msg); - uint8_t *data = msg->data; - - LOGP(DL1C, LOGL_INFO, "SIM %s\n", osmo_hexdump(data, len)); + LOGP(DL1C, LOGL_INFO, "SIM %s\n", msgb_hexdump_l1(msg)); sim_apdu_resp(ms, msg); - + return 0; } @@ -751,6 +832,7 @@ static int rx_l1_tch_mode_conf(struct osmocom_ms *ms, struct msgb *msg) mc.tch_mode = conf->tch_mode; mc.audio_mode = conf->audio_mode; + mc.tch_flags = conf->tch_flags; mc.ms = ms; osmo_signal_dispatch(SS_L1CTL, S_L1CTL_TCH_MODE_CONF, &mc); @@ -762,7 +844,6 @@ static int rx_l1_traffic_ind(struct osmocom_ms *ms, struct msgb *msg) { struct l1ctl_info_dl *dl; struct l1ctl_traffic_ind *ti; - size_t frame_len; uint8_t *frame; if (msgb_l1len(msg) < sizeof(*dl)) { @@ -779,11 +860,8 @@ static int rx_l1_traffic_ind(struct osmocom_ms *ms, struct msgb *msg) msg->l2h = dl->payload; msg->l3h = frame; - /* Calculate the frame length */ - frame_len = msgb_l3len(msg); - - DEBUGP(DL1C, "TRAFFIC IND len=%zu (%s)\n", frame_len, - osmo_hexdump(frame, frame_len)); + LOGP(DL1C, LOGL_DEBUG, "Rx TRAFFIC.ind (fn=%u, chan_nr=0x%02x, len=%u): %s\n", + ntohl(dl->frame_nr), dl->chan_nr, msgb_l3len(msg), msgb_hexdump_l3(msg)); /* distribute or drop */ if (ms->l1_entity.l1_traffic_ind) @@ -800,7 +878,6 @@ int l1ctl_tx_traffic_req(struct osmocom_ms *ms, struct msgb *msg, struct l1ctl_hdr *l1h; struct l1ctl_info_ul *l1i_ul; struct l1ctl_traffic_req *tr; - size_t frame_len; uint8_t *frame; /* Header handling */ @@ -808,20 +885,8 @@ int l1ctl_tx_traffic_req(struct osmocom_ms *ms, struct msgb *msg, frame = (uint8_t *) tr->data; msg->l3h = frame; - /* Calculate the frame length */ - frame_len = msgb_l3len(msg); - - DEBUGP(DL1C, "TRAFFIC REQ len=%zu (%s)\n", frame_len, - osmo_hexdump(frame, frame_len)); - - if ((frame[0] >> 4) != 0xd) { - LOGP(DL1C, LOGL_ERROR, "Traffic Request has incorrect magic " - "(%u != 0xd)\n", frame[0] >> 4); - msgb_free(msg); - return -EINVAL; - } - -// printf("TX %s\n", osmo_hexdump(frame, frame_len)); + LOGP(DL1C, LOGL_DEBUG, "Tx TRAFFIC.req (chan_nr=0x%02x, len=%u): %s\n", + chan_nr, msgb_l3len(msg), msgb_hexdump_l3(msg)); /* prepend uplink info header */ l1i_ul = (struct l1ctl_info_ul *) msgb_push(msg, sizeof(*l1i_ul)); @@ -884,6 +949,190 @@ static int rx_l1_neigh_pm_ind(struct osmocom_ms *ms, struct msgb *msg) return 0; } +/* Receive L1CTL_GPRS_UL_BLOCK_CNF */ +static int rx_l1_gprs_ul_block_cnf(struct osmocom_ms *ms, struct msgb *msg) +{ + const struct l1ctl_gprs_ul_block_cnf *cnf = (void *)msg->l1h; + + if (msgb_l1len(msg) < sizeof(*cnf)) { + LOGP(DL1C, LOGL_ERROR, + "Rx malformed GPRS UL BLOCK.cnf (len=%u < %zu)\n", + msgb_l1len(msg), sizeof(*cnf)); + return -EINVAL; + } + if (OSMO_UNLIKELY(cnf->tn >= 8)) { + LOGP(DL1C, LOGL_ERROR, + "Rx malformed GPRS UL BLOCK.cnf (tn=%u)\n", cnf->tn); + return -EINVAL; + } + + msg->l2h = (void *)&cnf->data[0]; + + DEBUGP(DL1C, "Rx GPRS UL BLOCK.cnf (fn=%u, tn=%u, len=%u): %s\n", + ntohl(cnf->fn), cnf->tn, msgb_l2len(msg), msgb_hexdump_l2(msg)); + + /* distribute or drop */ + if (ms->l1_entity.l1_gprs_ul_block_cnf) + return ms->l1_entity.l1_gprs_ul_block_cnf(ms, msg); + + msgb_free(msg); + return 0; +} + +/* Receive L1CTL_GPRS_DL_BLOCK_IND */ +static int rx_l1_gprs_dl_block_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + const struct l1ctl_gprs_dl_block_ind *ind = (void *)msg->l1h; + uint8_t gsmtap_chan; + uint32_t fn; + + if (msgb_l1len(msg) < sizeof(*ind)) { + LOGP(DL1C, LOGL_ERROR, + "Rx malformed GPRS DL BLOCK.ind (len=%u < %zu)\n", + msgb_l1len(msg), sizeof(*ind)); + return -EINVAL; + } + if (OSMO_UNLIKELY(ind->hdr.tn >= 8)) { + LOGP(DL1C, LOGL_ERROR, + "Rx malformed GPRS DL BLOCK.ind (tn=%u)\n", + ind->hdr.tn); + return -EINVAL; + } + + msg->l2h = (void *)&ind->data[0]; + + fn = ntohl(ind->hdr.fn); + if ((fn % 104) == 12) + gsmtap_chan = GSMTAP_CHANNEL_PTCCH; + else + gsmtap_chan = GSMTAP_CHANNEL_PDTCH; + + gsmtap_send(l23_cfg.gsmtap.inst, + ms->rrlayer.cd_now.arfcn, + ind->hdr.tn, gsmtap_chan, 0, fn, + rxlev2dbm(ind->meas.rx_lev), 0, + msgb_l2(msg), msgb_l2len(msg)); + + DEBUGP(DL1C, "Rx GPRS DL BLOCK.ind (fn=%u, tn=%u, len=%u): %s\n", + fn, ind->hdr.tn, msgb_l2len(msg), msgb_hexdump_l2(msg)); + + /* distribute or drop */ + if (ms->l1_entity.l1_gprs_dl_block_ind) + return ms->l1_entity.l1_gprs_dl_block_ind(ms, msg); + + msgb_free(msg); + return 0; +} + +/* Receive L1CTL_GPRS_RTS_IND */ +static int rx_l1_gprs_rts_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + const struct l1ctl_gprs_rts_ind *ind = (void *)msg->l1h; + + if (msgb_l1len(msg) < sizeof(*ind)) { + LOGP(DL1C, LOGL_ERROR, + "Rx malformed GPRS RTS.ind (len=%u < %zu)\n", + msgb_l1len(msg), sizeof(*ind)); + return -EINVAL; + } + if (OSMO_UNLIKELY(ind->tn >= 8)) { + LOGP(DL1C, LOGL_ERROR, + "Rx malformed GPRS RTS.ind (tn=%u)\n", + ind->tn); + return -EINVAL; + } + + DEBUGP(DL1C, "Rx GPRS RTS.ind (fn=%u, tn=%u, usf=%u)\n", + ntohl(ind->fn), ind->tn, ind->usf); + + /* distribute or drop */ + if (ms->l1_entity.l1_gprs_rts_ind) + return ms->l1_entity.l1_gprs_rts_ind(ms, msg); + + msgb_free(msg); + return 0; +} + +/* Transmit L1CTL_GPRS_UL_BLOCK_REQ */ +int l1ctl_tx_gprs_ul_block_req(struct osmocom_ms *ms, uint32_t fn, uint8_t tn, + const uint8_t *data, size_t data_len) +{ + struct l1ctl_gprs_ul_block_req *req; + struct msgb *msg; + + msg = osmo_l1_alloc(L1CTL_GPRS_UL_BLOCK_REQ); + if (!msg) + return -ENOMEM; + + req = (void *)msgb_put(msg, sizeof(*req)); + req->hdr.fn = htonl(fn); + req->hdr.tn = tn; + if (data_len > 0) + memcpy(msgb_put(msg, data_len), data, data_len); + + DEBUGP(DL1C, "Tx GPRS UL block (fn=%u, tn=%u, len=%zu): %s\n", + fn, tn, data_len, osmo_hexdump(data, data_len)); + + gsmtap_send(l23_cfg.gsmtap.inst, + ms->rrlayer.cd_now.arfcn | GSMTAP_ARFCN_F_UPLINK, + tn, GSMTAP_CHANNEL_PDTCH, 0, fn, 127, 0, + data, data_len); + + return osmo_send_l1(ms, msg); +} + +/* Transmit L1CTL_GPRS_UL_TBF_CFG_REQ */ +int l1ctl_tx_gprs_ul_tbf_cfg_req(struct osmocom_ms *ms, uint8_t tbf_ref, + uint8_t slotmask, uint32_t start_fn) +{ + struct l1ctl_gprs_ul_tbf_cfg_req *req; + struct msgb *msg; + + msg = osmo_l1_alloc(L1CTL_GPRS_UL_TBF_CFG_REQ); + if (!msg) + return -ENOMEM; + + req = (void *)msgb_put(msg, sizeof(*req)); + *req = (struct l1ctl_gprs_ul_tbf_cfg_req) { + .tbf_ref = tbf_ref, + .slotmask = slotmask, + .start_fn = htonl(start_fn), + }; + + DEBUGP(DL1C, "Tx GPRS UL TBF CFG: " + "tbf_ref=%u, slotmask=0x%02x, start_fn=%u\n", + tbf_ref, slotmask, start_fn); + + return osmo_send_l1(ms, msg); +} + +/* Transmit L1CTL_GPRS_DL_TBF_CFG_REQ */ +int l1ctl_tx_gprs_dl_tbf_cfg_req(struct osmocom_ms *ms, uint8_t tbf_ref, + uint8_t slotmask, uint32_t start_fn, + uint8_t dl_tfi) +{ + struct l1ctl_gprs_dl_tbf_cfg_req *req; + struct msgb *msg; + + msg = osmo_l1_alloc(L1CTL_GPRS_DL_TBF_CFG_REQ); + if (!msg) + return -ENOMEM; + + req = (void *)msgb_put(msg, sizeof(*req)); + *req = (struct l1ctl_gprs_dl_tbf_cfg_req) { + .tbf_ref = tbf_ref, + .slotmask = slotmask, + .start_fn = htonl(start_fn), + .dl_tfi = dl_tfi, + }; + + DEBUGP(DL1C, "Tx GPRS DL TBF CFG: " + "tbf_ref=%u, slotmask=0x%02x, start_fn=%u, dl_tfi=%u)\n", + tbf_ref, slotmask, start_fn, dl_tfi); + + return osmo_send_l1(ms, msg); +} + /* Receive incoming data from L1 using L1CTL format */ int l1ctl_recv(struct osmocom_ms *ms, struct msgb *msg) { @@ -952,6 +1201,15 @@ int l1ctl_recv(struct osmocom_ms *ms, struct msgb *msg) case L1CTL_TRAFFIC_CONF: msgb_free(msg); break; + case L1CTL_GPRS_UL_BLOCK_CNF: + rc = rx_l1_gprs_ul_block_cnf(ms, msg); + break; + case L1CTL_GPRS_DL_BLOCK_IND: + rc = rx_l1_gprs_dl_block_ind(ms, msg); + break; + case L1CTL_GPRS_RTS_IND: + rc = rx_l1_gprs_rts_ind(ms, msg); + break; default: LOGP(DL1C, LOGL_ERROR, "Unknown MSG: %u\n", hdr->msg_type); msgb_free(msg); |