diff options
Diffstat (limited to 'openbsc/src/abis_rsl.c')
-rw-r--r-- | openbsc/src/abis_rsl.c | 473 |
1 files changed, 350 insertions, 123 deletions
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c index 2ada2ac1e..053c71258 100644 --- a/openbsc/src/abis_rsl.c +++ b/openbsc/src/abis_rsl.c @@ -38,6 +38,8 @@ #include <openbsc/tlv.h> #include <openbsc/paging.h> #include <openbsc/signal.h> +#include <openbsc/meas_rep.h> +#include <openbsc/rtp_proxy.h> #define RSL_ALLOC_SIZE 1024 #define RSL_ALLOC_HEADROOM 128 @@ -205,32 +207,32 @@ struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr) if (ts->pchan != GSM_PCHAN_TCH_F && ts->pchan != GSM_PCHAN_PDCH && ts->pchan != GSM_PCHAN_TCH_F_PDCH) - fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n", + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", chan_nr, ts->pchan); } else if ((cbits & 0x1e) == 0x02) { lch_idx = cbits & 0x1; /* TCH/H */ if (ts->pchan != GSM_PCHAN_TCH_H) - fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n", + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", chan_nr, ts->pchan); } else if ((cbits & 0x1c) == 0x04) { lch_idx = cbits & 0x3; /* SDCCH/4 */ if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4) - fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n", + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", chan_nr, ts->pchan); } else if ((cbits & 0x18) == 0x08) { lch_idx = cbits & 0x7; /* SDCCH/8 */ if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C) - fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n", + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", chan_nr, ts->pchan); } else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) { lch_idx = 0; if (ts->pchan != GSM_PCHAN_CCCH && ts->pchan != GSM_PCHAN_CCCH_SDCCH4) - fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n", + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", chan_nr, ts->pchan); /* FIXME: we should not return first sdcch4 !!! */ } else { - fprintf(stderr, "unknown chan_nr=0x%02x\n", chan_nr); + LOGP(DRSL, LOGL_ERROR, "unknown chan_nr=0x%02x\n", chan_nr); return NULL; } @@ -240,7 +242,7 @@ struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr) } /* See Table 10.5.25 of GSM04.08 */ -u_int8_t lchan2chan_nr(struct gsm_lchan *lchan) +u_int8_t lchan2chan_nr(const struct gsm_lchan *lchan) { struct gsm_bts_trx_ts *ts = lchan->ts; u_int8_t cbits, chan_nr; @@ -490,7 +492,7 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm, if (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN && lchan->tch_mode != GSM48_CMODE_SIGN) - DEBUGP(DRSL, "unsupported: rsl_mode == signalling, " + LOGP(DRSL, LOGL_ERROR, "unsupported: rsl_mode == signalling, " "but tch_mode != signalling\n"); switch (lchan->type) { @@ -575,7 +577,7 @@ int rsl_chan_activate(struct gsm_bts_trx *trx, u_int8_t chan_nr, #endif int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type, - u_int8_t ta) + u_int8_t ta, u_int8_t ho_ref) { struct abis_rsl_dchan_hdr *dh; struct msgb *msg; @@ -602,9 +604,9 @@ int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type, dh->chan_nr = chan_nr; msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type); - /* For compatibility with Phase 1 */ msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm), (u_int8_t *) &cm); + /* For compatibility with Phase 1 */ msgb_tlv_put(msg, RSL_IE_CHAN_IDENT, 4, (u_int8_t *) &ci); @@ -615,6 +617,15 @@ int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type, msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info); } + switch (act_type) { + case RSL_ACT_INTER_ASYNC: + case RSL_ACT_INTER_SYNC: + msgb_tv_put(msg, RSL_IE_HANDO_REF, ho_ref); + break; + default: + break; + } + msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power); msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power); msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta); @@ -838,7 +849,7 @@ int rsl_data_request(struct msgb *msg, u_int8_t link_id) struct abis_rsl_rll_hdr *rh; if (msg->lchan == NULL) { - fprintf(stderr, "cannot send DATA REQUEST to unknown lchan\n"); + LOGP(DRSL, LOGL_ERROR, "cannot send DATA REQUEST to unknown lchan\n"); return -EINVAL; } @@ -910,7 +921,11 @@ static int rsl_rx_chan_act_ack(struct msgb *msg) * to assign the activated channel to the MS */ if (rslh->ie_chan != RSL_IE_CHAN_NR) return -EINVAL; - + + msg->lchan->state = LCHAN_S_ACTIVE; + + dispatch_signal(SS_LCHAN, S_LCHAN_ACTIVATE_ACK, msg->lchan); + return 0; } @@ -929,6 +944,10 @@ static int rsl_rx_chan_act_nack(struct msgb *msg) print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE), TLVP_LEN(&tp, RSL_IE_CAUSE)); + msg->lchan->state = LCHAN_S_NONE; + + dispatch_signal(SS_LCHAN, S_LCHAN_ACTIVATE_NACK, msg->lchan); + lchan_free(msg->lchan); return 0; } @@ -939,7 +958,8 @@ static int rsl_rx_conn_fail(struct msgb *msg) struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct tlv_parsed tp; - DEBUGPC(DRSL, "CONNECTION FAIL: "); + /* FIXME: print which channel */ + LOGP(DRSL, LOGL_NOTICE, "CONNECTION FAIL: RELEASING\n"); rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); @@ -947,53 +967,144 @@ static int rsl_rx_conn_fail(struct msgb *msg) print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE), TLVP_LEN(&tp, RSL_IE_CAUSE)); - DEBUGPC(DRSL, "RELEASING.\n"); - /* FIXME: only free it after channel release ACK */ return rsl_rf_chan_release(msg->lchan); } +static void print_meas_rep_uni(struct gsm_meas_rep_unidir *mru, + const char *prefix) +{ + DEBUGPC(DMEAS, "RXL-FULL-%s=%3ddBm RXL-SUB-%s=%3ddBm ", + prefix, rxlev2dbm(mru->full.rx_lev), + prefix, rxlev2dbm(mru->sub.rx_lev)); + DEBUGPC(DMEAS, "RXQ-FULL-%s=%d RXQ-SUB-%s=%d ", + prefix, mru->full.rx_qual, prefix, mru->sub.rx_qual); +} + +static void print_meas_rep(struct gsm_meas_rep *mr) +{ + int i; + + DEBUGP(DMEAS, "MEASUREMENT RESULT NR=%d ", mr->nr); + + if (mr->flags & MEAS_REP_F_DL_DTX) + DEBUGPC(DMEAS, "DTXd "); + + print_meas_rep_uni(&mr->ul, "ul"); + DEBUGPC(DMEAS, "BS_POWER=%d ", mr->bs_power); + if (mr->flags & MEAS_REP_F_MS_TO) + DEBUGPC(DMEAS, "MS_TO=%d ", mr->ms_timing_offset); + + if (mr->flags & MEAS_REP_F_MS_L1) { + DEBUGPC(DMEAS, "L1_MS_PWR=%3ddBm ", mr->ms_l1.pwr); + DEBUGPC(DMEAS, "L1_FPC=%u ", + mr->flags & MEAS_REP_F_FPC ? 1 : 0); + DEBUGPC(DMEAS, "L1_TA=%u ", mr->ms_l1.ta); + } + + if (mr->flags & MEAS_REP_F_UL_DTX) + DEBUGPC(DMEAS, "DTXu "); + if (mr->flags & MEAS_REP_F_BA1) + DEBUGPC(DMEAS, "BA1 "); + if (!(mr->flags & MEAS_REP_F_DL_VALID)) + DEBUGPC(DMEAS, "NOT VALID "); + else + print_meas_rep_uni(&mr->dl, "dl"); + + DEBUGPC(DMEAS, "NUM_NEIGH=%u\n", mr->num_cell); + if (mr->num_cell == 7) + return; + for (i = 0; i < mr->num_cell; i++) { + struct gsm_meas_rep_cell *mrc = &mr->cell[i]; + DEBUGP(DMEAS, "ARFCN=%u BSIC=%u => %d dBm\n", mrc->arfcn, mrc->bsic, + rxlev2dbm(mrc->rxlev)); + } +} + static int rsl_rx_meas_res(struct msgb *msg) { struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct tlv_parsed tp; + struct gsm_meas_rep *mr = lchan_next_meas_rep(msg->lchan); + u_int8_t len; + const u_int8_t *val; + int rc; + + /* check if this channel is actually active */ + /* FIXME: maybe this check should be way more generic/centralized */ + if (msg->lchan->state != LCHAN_S_ACTIVE) + return 0; + + memset(mr, 0, sizeof(*mr)); + mr->lchan = msg->lchan; - DEBUGPC(DMEAS, "MEASUREMENT RESULT "); rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); - if (TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR)) - DEBUGPC(DMEAS, "NR=%d ", *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR)); - if (TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS)) { - u_int8_t len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS); - const u_int8_t *val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS); - if (len >= 3) { - if (val[0] & 0x40) - DEBUGPC(DMEAS, "DTXd "); - DEBUGPC(DMEAS, "RXL-FULL-up=%d RXL-SUB-up=%d ", - val[0] & 0x3f, val[1] & 0x3f); - DEBUGPC(DMEAS, "RXQ-FULL-up=%d RXQ-SUB-up=%d ", - val[2]>>3 & 0x7, val[2] & 0x7); - } + if (!TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR) || + !TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS) || + !TLVP_PRESENT(&tp, RSL_IE_BS_POWER)) + return -EIO; + + /* Mandatory Parts */ + mr->nr = *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR); + + len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS); + val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS); + if (len >= 3) { + if (val[0] & 0x40) + mr->flags |= MEAS_REP_F_DL_DTX; + mr->ul.full.rx_lev = val[0] & 0x3f; + mr->ul.sub.rx_lev = val[1] & 0x3f; + mr->ul.full.rx_qual = val[2]>>3 & 0x7; + mr->ul.sub.rx_qual = val[2] & 0x7; } - if (TLVP_PRESENT(&tp, RSL_IE_BS_POWER)) - DEBUGPC(DMEAS, "BS_POWER=%d ", *TLVP_VAL(&tp, RSL_IE_BS_POWER)); + + mr->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER); + + /* Optional Parts */ if (TLVP_PRESENT(&tp, RSL_IE_MS_TIMING_OFFSET)) - DEBUGPC(DMEAS, "MS_TO=%d ", - *TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET)); + mr->ms_timing_offset = + *TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET); + if (TLVP_PRESENT(&tp, RSL_IE_L1_INFO)) { - const u_int8_t *val = TLVP_VAL(&tp, RSL_IE_L1_INFO); - u_int8_t pwr_lvl = val[0] >> 3; - DEBUGPC(DMEAS, "L1_MS_PWR=%ddBm ", - ms_pwr_dbm(msg->trx->bts->band, pwr_lvl)); - DEBUGPC(DMEAS, "L1_FPC=%u ", val[0] & 0x04 ? 1 : 0); - DEBUGPC(DMEAS, "L1_TA=%u ", val[1]); + val = TLVP_VAL(&tp, RSL_IE_L1_INFO); + mr->flags |= MEAS_REP_F_MS_L1; + mr->ms_l1.pwr = ms_pwr_dbm(msg->trx->bts->band, val[0] >> 3); + if (val[0] & 0x04) + mr->flags |= MEAS_REP_F_FPC; + mr->ms_l1.ta = val[1]; } if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) { - DEBUGPC(DMEAS, "L3\n"); msg->l3h = (u_int8_t *) TLVP_VAL(&tp, RSL_IE_L3_INFO); - return gsm0408_rcvmsg(msg, 0); - } else - DEBUGPC(DMEAS, "\n"); + rc = gsm48_parse_meas_rep(mr, msg); + if (rc < 0) + return rc; + } + + print_meas_rep(mr); + + dispatch_signal(SS_LCHAN, S_LCHAN_MEAS_REP, mr); + + return 0; +} + +/* Chapter 8.4.7 */ +static int rsl_rx_hando_det(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); + struct tlv_parsed tp; + + DEBUGP(DRSL, "HANDOVER DETECT "); + + rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); + + if (TLVP_PRESENT(&tp, RSL_IE_ACCESS_DELAY)) + DEBUGPC(DRSL, "access delay = %u\n", + *TLVP_VAL(&tp, RSL_IE_ACCESS_DELAY)); + else + DEBUGPC(DRSL, "\n"); + + dispatch_signal(SS_LCHAN, S_LCHAN_HANDOVER_DETECT, msg->lchan); return 0; } @@ -1025,8 +1136,12 @@ static int abis_rsl_rx_dchan(struct msgb *msg) case RSL_MT_MEAS_RES: rc = rsl_rx_meas_res(msg); break; + case RSL_MT_HANDO_DET: + rc = rsl_rx_hando_det(msg); + break; case RSL_MT_RF_CHAN_REL_ACK: DEBUGPC(DRSL, "RF CHANNEL RELEASE ACK\n"); + msg->lchan->state = LCHAN_S_NONE; lchan_free(msg->lchan); break; case RSL_MT_MODE_MODIFY_ACK: @@ -1072,7 +1187,7 @@ static int rsl_rx_error_rep(struct msgb *msg) struct abis_rsl_common_hdr *rslh = msgb_l2(msg); struct tlv_parsed tp; - DEBUGP(DRSL, "ERROR REPORT "); + LOGP(DRSL, LOGL_ERROR, "ERROR REPORT "); rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg)-sizeof(*rslh)); @@ -1080,7 +1195,7 @@ static int rsl_rx_error_rep(struct msgb *msg) print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE), TLVP_LEN(&tp, RSL_IE_CAUSE)); - DEBUGPC(DRSL, "\n"); + LOGPC(DRSL, LOGL_ERROR, "\n"); return 0; } @@ -1100,7 +1215,7 @@ static int abis_rsl_rx_trx(struct msgb *msg) break; case RSL_MT_OVERLOAD: /* indicate CCCH / ACCH / processor overload */ - DEBUGP(DRSL, "TRX: CCCH/ACCH/CPU Overload\n"); + LOGP(DRSL, LOGL_ERROR, "TRX: CCCH/ACCH/CPU Overload\n"); break; default: DEBUGP(DRSL, "Unknown Abis RSL TRX message type 0x%02x\n", @@ -1150,11 +1265,14 @@ static int rsl_rx_chan_rqd(struct msgb *msg) lctype = get_ctype_by_chreq(bts, rqd_ref->ra, bts->network->neci); chreq_reason = get_reason_by_chreq(bts, rqd_ref->ra, bts->network->neci); + bts->network->stats.chreq.total++; + /* check availability / allocate channel */ lchan = lchan_alloc(bts, lctype); if (!lchan) { - DEBUGP(DRSL, "CHAN RQD: no resources for %u 0x%x\n", - lctype, rqd_ref->ra); + DEBUGP(DRSL, "CHAN RQD: no resources for %s 0x%x\n", + gsm_lchan_name(lctype), rqd_ref->ra); + bts->network->stats.chreq.no_channel++; /* FIXME: send some kind of reject ?!? */ return -ENOMEM; } @@ -1168,7 +1286,7 @@ static int rsl_rx_chan_rqd(struct msgb *msg) lchan->bs_power = 0; /* 0dB reduction, output power = Pn */ lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; lchan->tch_mode = GSM48_CMODE_SIGN; - rsl_chan_activate_lchan(lchan, 0x00, rqd_ta); + rsl_chan_activate_lchan(lchan, 0x00, rqd_ta, 0); /* create IMMEDIATE ASSIGN 04.08 messge */ memset(&ia, 0, sizeof(ia)); @@ -1251,12 +1369,12 @@ static int abis_rsl_rx_cchan(struct msgb *msg) /* CCCH overloaded, IMM_ASSIGN was dropped */ case RSL_MT_CBCH_LOAD_IND: /* current load on the CBCH */ - fprintf(stderr, "Unimplemented Abis RSL TRX message type " - "0x%02x\n", rslh->c.msg_type); + LOGP(DRSL, LOGL_NOTICE, "Unimplemented Abis RSL TRX message " + "type 0x%02x\n", rslh->c.msg_type); break; default: - fprintf(stderr, "Unknown Abis RSL TRX message type 0x%02x\n", - rslh->c.msg_type); + LOGP(DRSL, LOGL_NOTICE, "Unknown Abis RSL TRX message type " + "0x%02x\n", rslh->c.msg_type); return -EINVAL; } @@ -1268,7 +1386,7 @@ static int rsl_rx_rll_err_ind(struct msgb *msg) struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); u_int8_t *rlm_cause = rllh->data; - DEBUGPC(DRLL, "ERROR INDICATION cause=0x%02x\n", rlm_cause[1]); + LOGP(DRLL, LOGL_ERROR, "ERROR INDICATION cause=0x%02x\n", rlm_cause[1]); rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND); @@ -1349,38 +1467,111 @@ static int abis_rsl_rx_rll(struct msgb *msg) rc = rsl_rx_rll_err_ind(msg); break; case RSL_MT_UNIT_DATA_IND: - DEBUGPC(DRLL, "unimplemented Abis RLL message type 0x%02x\n", - rllh->c.msg_type); + LOGP(DRLL, LOGL_NOTICE, "unimplemented Abis RLL message " + "type 0x%02x\n", rllh->c.msg_type); break; default: - DEBUGPC(DRLL, "unknown Abis RLL message type 0x%02x\n", - rllh->c.msg_type); + LOGP(DRLL, LOGL_NOTICE, "unknown Abis RLL message " + "type 0x%02x\n", rllh->c.msg_type); } return rc; } -static u_int8_t ipa_smod_s_for_tch_mode(u_int8_t tch_mode) +static u_int8_t ipa_smod_s_for_lchan(struct gsm_lchan *lchan) { - switch (tch_mode) { + switch (lchan->tch_mode) { case GSM48_CMODE_SPEECH_V1: - return 0x00; + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + return 0x00; + case GSM_LCHAN_TCH_H: + return 0x03; + default: + break; + } case GSM48_CMODE_SPEECH_EFR: - return 0x01; + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + return 0x01; + /* there's no half-rate EFR */ + default: + break; + } case GSM48_CMODE_SPEECH_AMR: - return 0x02; - /* FIXME: Type1 half-rate and type3 half-rate */ + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + return 0x02; + case GSM_LCHAN_TCH_H: + return 0x05; + default: + break; + } + default: + break; } - DEBUGPC(DRSL, "Cannot determine ip.access speech mode for " - "tch_mode == 0x%02x\n", tch_mode); + LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access speech mode for " + "tch_mode == 0x%02x\n", lchan->tch_mode); return 0; } /* ip.access specific RSL extensions */ +static void ipac_parse_rtp(struct gsm_lchan *lchan, struct tlv_parsed *tv) +{ + struct in_addr ip; + u_int16_t port, conn_id; + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_IP)) { + ip.s_addr = *((u_int32_t *) TLVP_VAL(tv, RSL_IE_IPAC_LOCAL_IP)); + DEBUGPC(DRSL, "LOCAL_IP=%s ", inet_ntoa(ip)); + lchan->abis_ip.bound_ip = ntohl(ip.s_addr); + } + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_PORT)) { + port = *((u_int16_t *) TLVP_VAL(tv, RSL_IE_IPAC_LOCAL_PORT)); + port = ntohs(port); + DEBUGPC(DRSL, "LOCAL_PORT=%u ", port); + lchan->abis_ip.bound_port = port; + } + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_CONN_ID)) { + conn_id = *((u_int16_t *) TLVP_VAL(tv, RSL_IE_IPAC_CONN_ID)); + conn_id = ntohs(conn_id); + DEBUGPC(DRSL, "CON_ID=%u ", conn_id); + lchan->abis_ip.conn_id = conn_id; + } + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_RTP_PAYLOAD2)) { + lchan->abis_ip.rtp_payload2 = + *TLVP_VAL(tv, RSL_IE_IPAC_RTP_PAYLOAD2); + DEBUGPC(DRSL, "RTP_PAYLOAD2=0x%02x ", + lchan->abis_ip.rtp_payload2); + } + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_SPEECH_MODE)) { + lchan->abis_ip.speech_mode = + *TLVP_VAL(tv, RSL_IE_IPAC_SPEECH_MODE); + DEBUGPC(DRSL, "speech_mode=0x%02x ", + lchan->abis_ip.speech_mode); + } + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_IP)) { + ip.s_addr = *((u_int32_t *) TLVP_VAL(tv, RSL_IE_IPAC_REMOTE_IP)); + DEBUGPC(DRSL, "REMOTE_IP=%s ", inet_ntoa(ip)); + lchan->abis_ip.connect_ip = ntohl(ip.s_addr); + } + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_PORT)) { + port = *((u_int16_t *) TLVP_VAL(tv, RSL_IE_IPAC_REMOTE_PORT)); + port = ntohs(port); + DEBUGPC(DRSL, "REMOTE_PORT=%u ", port); + lchan->abis_ip.connect_port = port; + } +} + int rsl_ipacc_crcx(struct gsm_lchan *lchan) { struct msgb *msg = rsl_msgb_alloc(); struct abis_rsl_dchan_hdr *dh; - u_int8_t speech_mode; dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_IPAC_CRCX); @@ -1388,12 +1579,12 @@ int rsl_ipacc_crcx(struct gsm_lchan *lchan) dh->chan_nr = lchan2chan_nr(lchan); /* 0x1- == receive-only, 0x-1 == EFR codec */ - speech_mode = 0x10 | ipa_smod_s_for_tch_mode(lchan->tch_mode); - msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, speech_mode); + lchan->abis_ip.speech_mode = 0x10 | ipa_smod_s_for_lchan(lchan); + msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode); DEBUGP(DRSL, "channel=%s chan_nr=0x%02x IPAC_BIND " "speech_mode=0x%02x\n", gsm_ts_name(lchan->ts), - dh->chan_nr, speech_mode); + dh->chan_nr, lchan->abis_ip.speech_mode); msg->trx = lchan->ts->trx; @@ -1401,12 +1592,11 @@ int rsl_ipacc_crcx(struct gsm_lchan *lchan) } int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port, - u_int16_t conn_id, u_int8_t rtp_payload2) + u_int8_t rtp_payload2) { struct msgb *msg = rsl_msgb_alloc(); struct abis_rsl_dchan_hdr *dh; - u_int8_t *att_f8, *att_ip, *att_port; - u_int8_t speech_mode; + u_int32_t *att_ip; struct in_addr ia; dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); @@ -1414,34 +1604,26 @@ int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port, dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS; dh->chan_nr = lchan2chan_nr(lchan); + /* we need to store these now as MDCX_ACK does not return them :( */ + lchan->abis_ip.rtp_payload2 = rtp_payload2; + lchan->abis_ip.connect_port = port; + lchan->abis_ip.connect_ip = ip; + /* 0x0- == both directions, 0x-1 == EFR codec */ - speech_mode = 0x00 | ipa_smod_s_for_tch_mode(lchan->tch_mode); + lchan->abis_ip.speech_mode = 0x00 | ipa_smod_s_for_lchan(lchan); ia.s_addr = htonl(ip); DEBUGP(DRSL, "channel=%s chan_nr=0x%02x IPAC_MDCX " "IP=%s PORT=%d RTP_PAYLOAD2=%d CONN_ID=%d speech_mode=0x%02x\n", - gsm_ts_name(lchan->ts), dh->chan_nr, - inet_ntoa(ia), port, rtp_payload2, conn_id, speech_mode); - - att_f8 = msgb_put(msg, sizeof(conn_id)+1); - att_f8[0] = RSL_IE_IPAC_CONN_ID; - att_f8[1] = conn_id >> 8; - att_f8[2] = conn_id & 0xff; - - att_ip = msgb_put(msg, sizeof(ip)+1); - att_ip[0] = RSL_IE_IPAC_REMOTE_IP; - att_ip[1] = ip >> 24; - att_ip[2] = ip >> 16; - att_ip[3] = ip >> 8; - att_ip[4] = ip & 0xff; - //att_ip[4] = 11; - - att_port = msgb_put(msg, sizeof(port)+1); - att_port[0] = RSL_IE_IPAC_REMOTE_PORT; - att_port[1] = port >> 8; - att_port[2] = port & 0xff; - - msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, speech_mode); + gsm_ts_name(lchan->ts), dh->chan_nr, inet_ntoa(ia), port, + rtp_payload2, lchan->abis_ip.conn_id, lchan->abis_ip.speech_mode); + + msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id); + msgb_v_put(msg, RSL_IE_IPAC_REMOTE_IP); + att_ip = (u_int32_t *) msgb_put(msg, sizeof(ip)); + *att_ip = ia.s_addr; + msgb_tv16_put(msg, RSL_IE_IPAC_REMOTE_PORT, port); + msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode); if (rtp_payload2) msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD2, rtp_payload2); @@ -1450,6 +1632,20 @@ int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port, return abis_rsl_sendmsg(msg); } +/* tell BTS to connect RTP stream to our local RTP socket */ +int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan) +{ + struct rtp_socket *rs = lchan->abis_ip.rtp_socket; + int rc; + + rc = rsl_ipacc_mdcx(lchan, ntohl(rs->rtp.sin_local.sin_addr.s_addr), + ntohs(rs->rtp.sin_local.sin_port), + /* FIXME: use RTP payload of bound socket, not BTS*/ + lchan->abis_ip.rtp_payload2); + + return rc; +} + int rsl_ipacc_pdch_activate(struct gsm_lchan *lchan) { struct msgb *msg = rsl_msgb_alloc(); @@ -1473,8 +1669,6 @@ static int abis_rsl_rx_ipacc_crcx_ack(struct msgb *msg) struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct tlv_parsed tv; struct gsm_lchan *lchan = msg->lchan; - struct in_addr ip; - u_int16_t port, attr_f8; /* the BTS has acknowledged a local bind, it now tells us the IP * address and port number to which it has bound the given logical @@ -1484,37 +1678,62 @@ static int abis_rsl_rx_ipacc_crcx_ack(struct msgb *msg) if (!TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_PORT) || !TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_IP) || !TLVP_PRESENT(&tv, RSL_IE_IPAC_CONN_ID)) { - DEBUGPC(DRSL, "mandatory IE missing"); + LOGP(DRSL, LOGL_NOTICE, "mandatory IE missing"); return -EINVAL; } - ip.s_addr = *((u_int32_t *) TLVP_VAL(&tv, RSL_IE_IPAC_LOCAL_IP)); - port = *((u_int16_t *) TLVP_VAL(&tv, RSL_IE_IPAC_LOCAL_PORT)); - attr_f8 = *((u_int16_t *) TLVP_VAL(&tv, 0xf8)); - DEBUGPC(DRSL, "IP=%s PORT=%d CONN_ID=%d ", - inet_ntoa(ip), ntohs(port), ntohs(attr_f8)); + ipac_parse_rtp(lchan, &tv); - if (TLVP_PRESENT(&tv, RSL_IE_IPAC_RTP_PAYLOAD2)) { - lchan->abis_ip.rtp_payload2 = - *TLVP_VAL(&tv, RSL_IE_IPAC_RTP_PAYLOAD2); - DEBUGPC(DRSL, "RTP_PAYLOAD2=0x%02x ", - lchan->abis_ip.rtp_payload2); - } + /* in case we don't use direct BTS-to-BTS RTP */ + if (!ipacc_rtp_direct) { + int rc; + /* the BTS has successfully bound a TCH to a local ip/port, + * which means we can connect our UDP socket to it */ + if (lchan->abis_ip.rtp_socket) { + rtp_socket_free(lchan->abis_ip.rtp_socket); + lchan->abis_ip.rtp_socket = NULL; + } - /* update our local information about this TS */ - lchan->abis_ip.bound_ip = ntohl(ip.s_addr); - lchan->abis_ip.bound_port = ntohs(port); - lchan->abis_ip.conn_id = ntohs(attr_f8); + lchan->abis_ip.rtp_socket = rtp_socket_create(); + if (!lchan->abis_ip.rtp_socket) + goto out_err; + + rc = rtp_socket_connect(lchan->abis_ip.rtp_socket, + lchan->abis_ip.bound_ip, + lchan->abis_ip.bound_port); + if (rc < 0) + goto out_err; + } dispatch_signal(SS_ABISIP, S_ABISIP_CRCX_ACK, msg->lchan); return 0; +out_err: + return -EIO; +} + +static int abis_rsl_rx_ipacc_mdcx_ack(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); + struct tlv_parsed tv; + struct gsm_lchan *lchan = msg->lchan; + + /* the BTS has acknowledged a remote connect request and + * it now tells us the IP address and port number to which it has + * connected the given logical channel */ + + rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); + ipac_parse_rtp(lchan, &tv); + dispatch_signal(SS_ABISIP, S_ABISIP_MDCX_ACK, msg->lchan); + + return 0; } static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg) { struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct tlv_parsed tv; + struct gsm_lchan *lchan = msg->lchan; rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); @@ -1522,6 +1741,12 @@ static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg) print_rsl_cause(TLVP_VAL(&tv, RSL_IE_CAUSE), TLVP_LEN(&tv, RSL_IE_CAUSE)); + /* the BTS tells us a RTP stream has been disconnected */ + if (lchan->abis_ip.rtp_socket) { + rtp_socket_free(lchan->abis_ip.rtp_socket); + lchan->abis_ip.rtp_socket = NULL; + } + dispatch_signal(SS_ABISIP, S_ABISIP_DLCX_IND, msg->lchan); return 0; @@ -1549,6 +1774,7 @@ static int abis_rsl_rx_ipacc(struct msgb *msg) case RSL_MT_IPAC_MDCX_ACK: /* the BTS tells us that a connect operation was successful */ DEBUGPC(DRSL, "IPAC_MDCX_ACK "); + rc = abis_rsl_rx_ipacc_mdcx_ack(msg); break; case RSL_MT_IPAC_MDCX_NACK: /* somehow the BTS was unable to connect the lchan to a remote @@ -1560,7 +1786,8 @@ static int abis_rsl_rx_ipacc(struct msgb *msg) rc = abis_rsl_rx_ipacc_dlcx_ind(msg); break; default: - DEBUGPC(DRSL, "Unknown ip.access msg_type 0x%02x", rllh->c.msg_type); + LOGP(DRSL, LOGL_NOTICE, "Unknown ip.access msg_type 0x%02x", + rllh->c.msg_type); break; } DEBUGPC(DRSL, "\n"); @@ -1601,15 +1828,15 @@ int abis_rsl_rcvmsg(struct msgb *msg) rc = abis_rsl_rx_trx(msg); break; case ABIS_RSL_MDISC_LOC: - fprintf(stderr, "unimplemented RSL msg disc 0x%02x\n", + LOGP(DRSL, LOGL_NOTICE, "unimplemented RSL msg disc 0x%02x\n", rslh->msg_discr); break; case ABIS_RSL_MDISC_IPACCESS: rc = abis_rsl_rx_ipacc(msg); break; default: - fprintf(stderr, "unknown RSL message discriminator 0x%02x\n", - rslh->msg_discr); + LOGP(DRSL, LOGL_NOTICE, "unknown RSL message discriminator " + "0x%02x\n", rslh->msg_discr); return -EINVAL; } msgb_free(msg); @@ -1658,11 +1885,11 @@ int rsl_ccch_conf_to_bs_ccch_sdcch_comb(int ccch_conf) /* From Table 10.5.33 of GSM 04.08 */ int rsl_number_of_paging_subchannels(struct gsm_bts *bts) { - if (bts->chan_desc.ccch_conf == RSL_BCCH_CCCH_CONF_1_C) { - return MAX(1, (3 - bts->chan_desc.bs_ag_blks_res)) - * (bts->chan_desc.bs_pa_mfrms + 2); + if (bts->si_common.chan_desc.ccch_conf == RSL_BCCH_CCCH_CONF_1_C) { + return MAX(1, (3 - bts->si_common.chan_desc.bs_ag_blks_res)) + * (bts->si_common.chan_desc.bs_pa_mfrms + 2); } else { - return (9 - bts->chan_desc.bs_ag_blks_res) - * (bts->chan_desc.bs_pa_mfrms + 2); + return (9 - bts->si_common.chan_desc.bs_ag_blks_res) + * (bts->si_common.chan_desc.bs_pa_mfrms + 2); } } |