From bac087c207ab3323c354d173335c30c85396464e Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 1 Sep 2013 11:09:20 +0200 Subject: Add SDCCH/SACCH/FACCH messages to PH-/MPH-/TCH-SAP interface This part moves control channel message primitives from osmo-bts-sysmo to common part. In order to control ciphering fo BTS model, CIPHER (MPH_INFO) messages are used. --- src/common/l1sap.c | 181 ++++++++++++++++++++++++++++++++++++- src/osmo-bts-sysmo/l1_if.c | 219 ++++++++++++++++++--------------------------- 2 files changed, 267 insertions(+), 133 deletions(-) diff --git a/src/common/l1sap.c b/src/common/l1sap.c index 9068bb88..14dbde9b 100644 --- a/src/common/l1sap.c +++ b/src/common/l1sap.c @@ -69,6 +69,54 @@ struct msgb *l1sap_msgb_alloc(unsigned int l2_len) return msg; } +static int l1sap_tx_ciph_req(struct gsm_bts_trx *trx, uint8_t chan_nr, + uint8_t downlink, uint8_t uplink) +{ + struct osmo_phsap_prim l1sap_ciph; + + osmo_prim_init(&l1sap_ciph.oph, SAP_GSM_PH, PRIM_MPH_INFO, + PRIM_OP_REQUEST, NULL); + l1sap_ciph.u.info.type = PRIM_INFO_ACT_CIPH; + l1sap_ciph.u.info.u.ciph_req.chan_nr = chan_nr; + l1sap_ciph.u.info.u.ciph_req.downlink = downlink; + l1sap_ciph.u.info.u.ciph_req.uplink = uplink; + + return l1sap_down(trx, &l1sap_ciph); +} + + +/* check if the message is a GSM48_MT_RR_CIPH_M_CMD, and if yes, enable + * uni-directional de-cryption on the uplink. We need this ugly layering + * violation as we have no way of passing down L3 metadata (RSL CIPHERING CMD) + * to this point in L1 */ +static int check_for_ciph_cmd(struct msgb *msg, struct gsm_lchan *lchan, + uint8_t chan_nr) +{ + + /* only do this if we are in the right state */ + switch (lchan->ciph_state) { + case LCHAN_CIPH_NONE: + case LCHAN_CIPH_RX_REQ: + break; + default: + return 0; + } + + /* First byte (Address Field) of LAPDm header) */ + if (msg->data[0] != 0x03) + return 0; + /* First byte (protocol discriminator) of RR */ + if ((msg->data[3] & 0xF) != GSM48_PDISC_RR) + return 0; + /* 2nd byte (msg type) of RR */ + if ((msg->data[4] & 0x3F) != GSM48_MT_RR_CIPH_M_CMD) + return 0; + + l1sap_tx_ciph_req(lchan->ts->trx, chan_nr, 0, 1); + + return 1; +} + /* time information received from bts model */ static int l1sap_info_time_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap, @@ -193,10 +241,13 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx, { struct msgb *msg = l1sap->oph.msg; struct gsm_time g_time; + struct gsm_lchan *lchan; uint8_t chan_nr, link_id; - uint8_t tn; + uint8_t tn, ss; uint32_t fn; uint8_t *p, *si; + struct lapdm_entity *le; + struct osmo_phsap_prim pp; int rc; chan_nr = rts_ind->chan_nr; @@ -241,6 +292,52 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx, memcpy(p, si, GSM_MACBLOCK_LEN); else memcpy(p, fill_frame, GSM_MACBLOCK_LEN); + } else if (!(chan_nr & 0x80)) { /* only TCH/F, TCH/H, SDCCH/4 and SDCCH/8 have C5 bit cleared */ + if (L1SAP_IS_CHAN_TCHH(chan_nr)) + ss = L1SAP_CHAN2SS_TCHH(chan_nr); /* TCH/H */ + else if (L1SAP_IS_CHAN_SDCCH4(chan_nr)) + ss = L1SAP_CHAN2SS_SDCCH4(chan_nr); /* SDCCH/4 */ + else if (L1SAP_IS_CHAN_SDCCH8(chan_nr)) + ss = L1SAP_CHAN2SS_SDCCH8(chan_nr); /* SDCCH/8 */ + else + ss = 0; /* TCH/F */ + lchan = &trx->ts[tn].lchan[ss]; + if (L1SAP_IS_LINK_SACCH(link_id)) { + p = msgb_put(msg, GSM_MACBLOCK_LEN); + /* L1-header, if not set/modified by layer 1 */ + p[0] = lchan->ms_power; + p[1] = lchan->rqd_ta; + le = &lchan->lapdm_ch.lapdm_acch; + } else + le = &lchan->lapdm_ch.lapdm_dcch; + rc = lapdm_phsap_dequeue_prim(le, &pp); + if (rc < 0) { + if (L1SAP_IS_LINK_SACCH(link_id)) { + /* No SACCH data from LAPDM pending, send SACCH filling */ + uint8_t *si = lchan_sacch_get(lchan); + if (si) { + /* The +2 is empty space where the DSP inserts the L1 hdr */ + memcpy(p + 2, si, GSM_MACBLOCK_LEN - 2); + } else + memcpy(p + 2, fill_frame, GSM_MACBLOCK_LEN - 2); + } else if ((!L1SAP_IS_CHAN_TCHF(chan_nr) && !L1SAP_IS_CHAN_TCHH(chan_nr)) + || lchan->rsl_cmode == RSL_CMOD_SPD_SIGN) { + /* send fill frame only, if not TCH/x != Signalling, otherwise send empty frame */ + p = msgb_put(msg, GSM_MACBLOCK_LEN); + memcpy(p, fill_frame, GSM_MACBLOCK_LEN); + } /* else the message remains empty, so TCH frames are sent */ + } else { + /* The +2 is empty space where the DSP inserts the L1 hdr */ + if (L1SAP_IS_LINK_SACCH(link_id)) + memcpy(p + 2, pp.oph.msg->data + 2, GSM_MACBLOCK_LEN - 2); + else { + p = msgb_put(msg, GSM_MACBLOCK_LEN); + memcpy(p, pp.oph.msg->data, GSM_MACBLOCK_LEN); + /* check if it is a RR CIPH MODE CMD. if yes, enable RX ciphering */ + check_for_ciph_cmd(pp.oph.msg, lchan, chan_nr); + } + msgb_free(pp.oph.msg); + } } else if (L1SAP_IS_CHAN_AGCH_PCH(chan_nr)) { p = msgb_put(msg, GSM_MACBLOCK_LEN); #warning "TODO: Yet another assumption that BS_AG_BLKS_RES=1" @@ -352,12 +449,46 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx, return 0; } +/* process radio link timeout counter S */ +static void radio_link_timeout(struct gsm_lchan *lchan, int bad_frame) +{ + struct gsm_bts_role_bts *btsb = lchan->ts->trx->bts->role; + + /* if link loss criterion already reached */ + if (lchan->s == 0) { + DEBUGP(DMEAS, "%s radio link counter S already 0.\n", + gsm_lchan_name(lchan)); + return; + } + + if (bad_frame) { + /* count down radio link counter S */ + lchan->s--; + DEBUGP(DMEAS, "%s counting down radio link counter S=%d\n", + gsm_lchan_name(lchan), lchan->s); + if (lchan->s == 0) + rsl_tx_conn_fail(lchan, RSL_ERR_RADIO_LINK_FAIL); + return; + } + + if (lchan->s < btsb->radio_link_timeout) { + /* count up radio link counter S */ + lchan->s += 2; + if (lchan->s > btsb->radio_link_timeout) + lchan->s = btsb->radio_link_timeout; + DEBUGP(DMEAS, "%s counting up radio link counter S=%d\n", + gsm_lchan_name(lchan), lchan->s); + } +} + /* DATA received from bts model */ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap, struct ph_data_param *data_ind) { struct msgb *msg = l1sap->oph.msg; struct gsm_time g_time; + struct gsm_lchan *lchan; + struct lapdm_entity *le; uint8_t *data = msg->l2h; int len = msgb_l2len(msg); uint8_t chan_nr, link_id; @@ -397,7 +528,53 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx, return 0; } - return 0; + lchan = &trx->ts[tn].lchan[ss]; + + /* bad frame */ + if (len == 0) { + if (L1SAP_IS_LINK_SACCH(link_id)) + radio_link_timeout(lchan, 1); + return -EINVAL; + } + + if (L1SAP_IS_LINK_SACCH(link_id)) { + radio_link_timeout(lchan, 0); + le = &lchan->lapdm_ch.lapdm_acch; + /* save the SACCH L1 header in the lchan struct for RSL MEAS RES */ + if (len < 2) { + LOGP(DL1P, LOGL_NOTICE, "SACCH with size %u<2 !?!\n", + len); + return -EINVAL; + } + /* Some brilliant engineer decided that the ordering of + * fields on the Um interface is different from the + * order of fields in RLS. See TS 04.04 (Chapter 7.2) + * vs. TS 08.58 (Chapter 9.3.10). */ + lchan->meas.l1_info[0] = data[0] << 3; + lchan->meas.l1_info[0] |= ((data[0] >> 5) & 1) << 2; + lchan->meas.l1_info[1] = data[1]; + lchan->meas.flags |= LC_UL_M_F_L1_VALID; + } else + le = &lchan->lapdm_ch.lapdm_dcch; + + /* if this is the first valid message after enabling Rx + * decryption, we have to enable Tx encryption */ + if (lchan->ciph_state == LCHAN_CIPH_RX_CONF) { + /* HACK: check if it's an I frame, in order to + * ignore some still buffered/queued UI frames received + * before decryption was enabled */ + if (data[0] == 0x01 && (data[1] & 0x01) == 0) { + l1sap_tx_ciph_req(trx, chan_nr, 1, 0); + } + } + + /* SDCCH, SACCH and FACCH all go to LAPDm */ + msgb_pull(msg, (msg->l2h - msg->data)); + msg->l1h = NULL; + lapdm_phsap_up(&l1sap->oph, le); + + /* don't free, because we forwarded data */ + return 1; } /* TCH received from bts model */ diff --git a/src/osmo-bts-sysmo/l1_if.c b/src/osmo-bts-sysmo/l1_if.c index 3c0a6f48..3122936c 100644 --- a/src/osmo-bts-sysmo/l1_if.c +++ b/src/osmo-bts-sysmo/l1_if.c @@ -471,7 +471,11 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg, u32Fn = l1sap->u.data.fn; u8Tn = L1SAP_CHAN2TS(chan_nr); subCh = 0x1f; - if (L1SAP_IS_CHAN_TCHF(chan_nr)) { + if (L1SAP_IS_LINK_SACCH(link_id)) { + sapi = GsmL1_Sapi_Sacch; + if (!L1SAP_IS_CHAN_TCHF(chan_nr)) + subCh = l1sap_chan2ss(chan_nr); + } else if (L1SAP_IS_CHAN_TCHF(chan_nr)) { if (trx->ts[u8Tn].pchan == GSM_PCHAN_PDCH) { if (L1SAP_IS_PTCCH(u32Fn)) { sapi = GsmL1_Sapi_Ptcch; @@ -480,7 +484,20 @@ static int ph_data_req(struct gsm_bts_trx *trx, struct msgb *msg, sapi = GsmL1_Sapi_Pdtch; u8BlockNbr = L1SAP_FN2MACBLOCK(u32Fn); } + } else { + sapi = GsmL1_Sapi_FacchF; + u8BlockNbr = (u32Fn % 13) >> 2; } + } else if (L1SAP_IS_CHAN_TCHH(chan_nr)) { + subCh = L1SAP_CHAN2SS_TCHH(chan_nr); + sapi = GsmL1_Sapi_FacchH; + u8BlockNbr = (u32Fn % 26) >> 3; + } else if (L1SAP_IS_CHAN_SDCCH4(chan_nr)) { + subCh = L1SAP_CHAN2SS_SDCCH4(chan_nr); + sapi = GsmL1_Sapi_Sdcch; + } else if (L1SAP_IS_CHAN_SDCCH8(chan_nr)) { + subCh = L1SAP_CHAN2SS_SDCCH8(chan_nr); + sapi = GsmL1_Sapi_Sdcch; } else if (L1SAP_IS_CHAN_BCCH(chan_nr)) { sapi = GsmL1_Sapi_Bcch; } else if (L1SAP_IS_CHAN_AGCH_PCH(chan_nr)) { @@ -613,12 +630,27 @@ static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg, static int mph_info_req(struct gsm_bts_trx *trx, struct msgb *msg, struct osmo_phsap_prim *l1sap) { + struct femtol1_hdl *fl1 = trx_femtol1_hdl(trx); uint8_t u8Tn, ss; uint8_t chan_nr; struct gsm_lchan *lchan; int rc = 0; switch (l1sap->u.info.type) { + case PRIM_INFO_ACT_CIPH: + chan_nr = l1sap->u.info.u.ciph_req.chan_nr; + u8Tn = L1SAP_CHAN2TS(chan_nr); + ss = l1sap_chan2ss(chan_nr); + lchan = &trx->ts[u8Tn].lchan[ss]; + if (l1sap->u.info.u.ciph_req.uplink) { + l1if_set_ciphering(fl1, lchan, 0); + lchan->ciph_state = LCHAN_CIPH_RX_REQ; + } + if (l1sap->u.info.u.ciph_req.downlink) { + l1if_set_ciphering(fl1, lchan, 1); + lchan->ciph_state = LCHAN_CIPH_TXRX_REQ; + } + break; case PRIM_INFO_ACTIVATE: case PRIM_INFO_DEACTIVATE: case PRIM_INFO_MODIFY: @@ -711,6 +743,40 @@ static uint8_t chan_nr_by_sapi(enum gsm_phys_chan_config pchan, case GsmL1_Sapi_Bcch: cbits = 0x10; break; + case GsmL1_Sapi_Sacch: + switch(pchan) { + case GSM_PCHAN_TCH_F: + cbits = 0x01; + break; + case GSM_PCHAN_TCH_H: + cbits = 0x02 + subCh; + break; + case GSM_PCHAN_CCCH_SDCCH4: + cbits = 0x04 + subCh; + break; + case GSM_PCHAN_SDCCH8_SACCH8C: + cbits = 0x08 + subCh; + break; + default: + LOGP(DL1C, LOGL_ERROR, "SACCH for pchan %d?\n", + pchan); + return 0; + } + break; + case GsmL1_Sapi_Sdcch: + switch(pchan) { + case GSM_PCHAN_CCCH_SDCCH4: + cbits = 0x04 + subCh; + break; + case GSM_PCHAN_SDCCH8_SACCH8C: + cbits = 0x08 + subCh; + break; + default: + LOGP(DL1C, LOGL_ERROR, "SDCCH for pchan %d?\n", + pchan); + return 0; + } + break; case GsmL1_Sapi_Agch: case GsmL1_Sapi_Pch: cbits = 0x12; @@ -733,6 +799,12 @@ static uint8_t chan_nr_by_sapi(enum gsm_phys_chan_config pchan, case GsmL1_Sapi_TchH: cbits = 0x02 + subCh; break; + case GsmL1_Sapi_FacchF: + cbits = 0x01; + break; + case GsmL1_Sapi_FacchH: + cbits = 0x02 + subCh; + break; case GsmL1_Sapi_Ptcch: if (!L1SAP_IS_PTCCH(u32Fn)) { LOGP(DL1C, LOGL_FATAL, "Not expecting PTCCH at frame " @@ -767,11 +839,8 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1, struct msgb *resp_msg; GsmL1_PhDataReq_t *data_req; GsmL1_MsgUnitParam_t *msu_param; - struct lapdm_entity *le; - struct gsm_lchan *lchan; struct gsm_time g_time; uint32_t t3p; - struct osmo_phsap_prim pp; int rc; struct osmo_phsap_prim *l1sap; uint8_t chan_nr, link_id; @@ -782,7 +851,10 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1, rts_ind->subCh, rts_ind->u8Tn, rts_ind->u32Fn); if (chan_nr) { fn = rts_ind->u32Fn; - link_id = 0; + if (rts_ind->sapi == GsmL1_Sapi_Sacch) + link_id = 0x40; + else + link_id = 0; rc = msgb_trim(l1p_msg, sizeof(*l1sap)); if (rc < 0) MSGB_ABORT(l1p_msg, "No room for primitive\n"); @@ -917,27 +989,6 @@ empty_frame: goto tx; } -/* determine LAPDm entity inside LAPDm channel for given L1 sapi */ -static struct lapdm_entity *le_by_l1_sapi(struct lapdm_channel *lc, GsmL1_Sapi_t sapi) -{ - switch (sapi) { - case GsmL1_Sapi_Sacch: - return &lc->lapdm_acch; - default: - return &lc->lapdm_dcch; - } -} - -static uint8_t gen_link_id(GsmL1_Sapi_t l1_sapi, uint8_t lapdm_sapi) -{ - uint8_t c_bits = 0; - - if (l1_sapi == GsmL1_Sapi_Sacch) - c_bits = 0x40; - - return c_bits | (lapdm_sapi & 7); -} - static void dump_meas_res(int ll, GsmL1_MeasParam_t *m) { LOGPC(DL1C, ll, ", Meas: RSSI %-3.2f dBm, Qual %-3.2f dB, " @@ -961,46 +1012,11 @@ static int process_meas_res(struct gsm_lchan *lchan, GsmL1_MeasParam_t *m) return lchan_new_ul_meas(lchan, &ulm); } -/* process radio link timeout counter S */ -static void radio_link_timeout(struct gsm_lchan *lchan, int bad_frame) -{ - struct gsm_bts_role_bts *btsb = lchan->ts->trx->bts->role; - - /* if link loss criterion already reached */ - if (lchan->s == 0) { - DEBUGP(DMEAS, "%s radio link counter S already 0.\n", - gsm_lchan_name(lchan)); - return; - } - - if (bad_frame) { - /* count down radio link counter S */ - lchan->s--; - DEBUGP(DMEAS, "%s counting down radio link counter S=%d\n", - gsm_lchan_name(lchan), lchan->s); - if (lchan->s == 0) - rsl_tx_conn_fail(lchan, RSL_ERR_RADIO_LINK_FAIL); - return; - } - - if (lchan->s < btsb->radio_link_timeout) { - /* count up radio link counter S */ - lchan->s += 2; - if (lchan->s > btsb->radio_link_timeout) - lchan->s = btsb->radio_link_timeout; - DEBUGP(DMEAS, "%s counting up radio link counter S=%d\n", - gsm_lchan_name(lchan), lchan->s); - } -} - static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_ind, struct msgb *l1p_msg) { struct gsm_bts_trx *trx = fl1->priv; - struct osmo_phsap_prim pp; struct gsm_lchan *lchan; - struct lapdm_entity *le; - struct msgb *msg; uint8_t chan_nr, link_id; struct osmo_phsap_prim *l1sap; uint32_t fn; @@ -1020,6 +1036,12 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i chan_nr = chan_nr_by_sapi(trx->ts[data_ind->u8Tn].pchan, data_ind->sapi, data_ind->subCh, data_ind->u8Tn, data_ind->u32Fn); + if (!chan_nr) { + LOGP(DL1C, LOGL_ERROR, "PH-DATA-INDICATION for unknown sapi " + "%d\n", data_ind->sapi); + msgb_free(l1p_msg); + return ENOTSUP; + } fn = data_ind->u32Fn; link_id = (data_ind->sapi == GsmL1_Sapi_Sacch) ? 0x40 : 0x00; @@ -1043,16 +1065,6 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i switch (data_ind->sapi) { case GsmL1_Sapi_Sacch: - radio_link_timeout(lchan, (data_ind->msgUnitParam.u8Size == 0)); - if (data_ind->msgUnitParam.u8Size == 0) - break; - /* save the SACCH L1 header in the lchan struct for RSL MEAS RES */ - if (data_ind->msgUnitParam.u8Size < 2) { - LOGP(DL1C, LOGL_NOTICE, "SACCH with size %u<2 !?!\n", - data_ind->msgUnitParam.u8Size); - break; - } - /* * Handle power control */ @@ -1060,73 +1072,18 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i data_ind->msgUnitParam.u8Buffer[0] & 0x1f, data_ind->measParam.fRssi); - /* Some brilliant engineer decided that the ordering of - * fields on the Um interface is different from the - * order of fields in RLS. See TS 04.04 (Chapter 7.2) - * vs. TS 08.58 (Chapter 9.3.10). */ - lchan->meas.l1_info[0] = data_ind->msgUnitParam.u8Buffer[0] << 3; - lchan->meas.l1_info[0] |= ((data_ind->msgUnitParam.u8Buffer[0] >> 5) & 1) << 2; - lchan->meas.l1_info[1] = data_ind->msgUnitParam.u8Buffer[1]; - lchan->meas.flags |= LC_UL_M_F_L1_VALID; - /* fall-through */ - case GsmL1_Sapi_Sdcch: - case GsmL1_Sapi_FacchF: - case GsmL1_Sapi_FacchH: - /* Check and Re-check for the SACCH */ - if (data_ind->msgUnitParam.u8Size == 0) { - LOGP(DL1C, LOGL_NOTICE, "%s %s data is null.\n", - gsm_lchan_name(lchan), - get_value_string(femtobts_l1sapi_names, data_ind->sapi)); - break; - } - - check_for_first_ciphrd(fl1, &data_ind->msgUnitParam, lchan); - - /* SDCCH, SACCH and FACCH all go to LAPDm */ - le = le_by_l1_sapi(&lchan->lapdm_ch, data_ind->sapi); - /* allocate and fill LAPDm primitive */ - msg = msgb_alloc_headroom(128, 64, "PH-DATA.ind"); - osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA, - PRIM_OP_INDICATION, msg); - - /* copy over actual MAC block */ - msg->l2h = msgb_put(msg, data_ind->msgUnitParam.u8Size); - memcpy(msg->l2h, data_ind->msgUnitParam.u8Buffer, - data_ind->msgUnitParam.u8Size); - - /* LAPDm requires those... */ - pp.u.data.chan_nr = gsm_lchan2chan_nr(lchan); - pp.u.data.link_id = gen_link_id(data_ind->sapi, 0); - - /* feed into the LAPDm code of libosmogsm */ - rc = lapdm_phsap_up(&pp.oph, le); - break; - case GsmL1_Sapi_TchF: - case GsmL1_Sapi_TchH: - /* TCH speech frame handling */ - rc = l1if_tch_rx(trx, chan_nr, l1p_msg); - break; - case GsmL1_Sapi_Pdtch: - case GsmL1_Sapi_Pacch: - case GsmL1_Sapi_Ptcch: - break; - case GsmL1_Sapi_Idle: - /* nothing to send */ - break; - default: - LOGP(DL1C, LOGL_NOTICE, "Rx PH-DATA.ind for unknown L1 SAPI %s\n", - get_value_string(femtobts_l1sapi_names, data_ind->sapi)); break; } - if (!chan_nr) { - /* message was handled by old code, not by L1SAP */ + /* check for TCH */ + if (data_ind->sapi == GsmL1_Sapi_TchF + || data_ind->sapi == GsmL1_Sapi_TchH) { + /* TCH speech frame handling */ + rc = l1if_tch_rx(trx, chan_nr, l1p_msg); msgb_free(l1p_msg); return rc; } - /* if we proceed to this point, the message has to be handled via L1SAP */ - /* get data pointer and length */ data = data_ind->msgUnitParam.u8Buffer; len = data_ind->msgUnitParam.u8Size; -- cgit v1.2.3