diff --git a/src/gprs_bssgp_pcu.cpp b/src/gprs_bssgp_pcu.cpp index 374baf63..020df616 100644 --- a/src/gprs_bssgp_pcu.cpp +++ b/src/gprs_bssgp_pcu.cpp @@ -25,8 +25,13 @@ struct sgsn_instance *sgsn; void *tall_bsc_ctx; struct bssgp_bvc_ctx *bctx = NULL; struct gprs_nsvc *nsvc = NULL; +static int bvc_sig_reset = 0, bvc_reset = 0, bvc_unblocked = 0; extern uint16_t spoof_mcc, spoof_mnc; +struct osmo_timer_list bvc_timer; + +static void bvc_timeout(void *_priv); + int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp) { struct bssgp_ud_hdr *budh; @@ -295,6 +300,11 @@ int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struct bssgp break; case BSSGP_PDUT_BVC_RESET_ACK: LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_RESET_ACK\n"); + if (!bvc_sig_reset) + bvc_sig_reset = 1; + else + bvc_reset = 1; + bvc_timeout(NULL); break; case BSSGP_PDUT_PAGING_PS: LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_PS\n"); @@ -316,6 +326,8 @@ int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struct bssgp break; case BSSGP_PDUT_BVC_UNBLOCK_ACK: LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_UNBLOCK_ACK\n"); + bvc_unblocked = 1; + bvc_timeout(NULL); break; case BSSGP_PDUT_SGSN_INVOKE_TRACE: LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SGSN_INVOKE_TRACE\n"); @@ -362,7 +374,9 @@ int gprs_bssgp_pcu_rcvmsg(struct msgb *msg) /* look-up or create the BTS context for this BVC */ bctx = btsctx_by_bvci_nsei(ns_bvci, msgb_nsei(msg)); - if (!bctx && pdu_type != BSSGP_PDUT_BVC_RESET_ACK) + if (!bctx + && pdu_type != BSSGP_PDUT_BVC_RESET_ACK + && pdu_type != BSSGP_PDUT_BVC_UNBLOCK_ACK) { LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU " "type %u for unknown BVCI\n", msgb_nsei(msg), ns_bvci, @@ -438,14 +452,22 @@ static int nsvc_signal_cb(unsigned int subsys, unsigned int signal, case S_NS_UNBLOCK: if (!nsvc_unblocked) { nsvc_unblocked = 1; - LOGP(DPCU, LOGL_NOTICE, "NS-VC is unblocked.\n"); - bssgp_tx_bvc_reset(bctx, bctx->bvci, - BSSGP_CAUSE_PROTO_ERR_UNSPEC); + LOGP(DPCU, LOGL_NOTICE, "NS-VC %d is unblocked.\n", + nsvc); + bvc_sig_reset = 0; + bvc_reset = 0; + bvc_unblocked = 0; + bvc_timeout(NULL); } break; case S_NS_BLOCK: if (nsvc_unblocked) { nsvc_unblocked = 0; + if (osmo_timer_pending(&bvc_timer)) + osmo_timer_del(&bvc_timer); + bvc_sig_reset = 0; + bvc_reset = 0; + bvc_unblocked = 0; LOGP(DPCU, LOGL_NOTICE, "NS-VC is blocked.\n"); } break; @@ -454,6 +476,52 @@ static int nsvc_signal_cb(unsigned int subsys, unsigned int signal, return 0; } +int gprs_bssgp_tx_fc_bvc(void) +{ + if (!bctx) { + LOGP(DBSSGP, LOGL_ERROR, "No bctx\n"); + return -EIO; + } + /* FIXME: use real values */ + return bssgp_tx_fc_bvc(bctx, 1, 6553500, 819100, 50000, 50000, + NULL, NULL); +// return bssgp_tx_fc_bvc(bctx, 1, 84000, 25000, 48000, 45000, +// NULL, NULL); +} + +static void bvc_timeout(void *_priv) +{ + struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + + if (!bvc_sig_reset) { + LOGP(DBSSGP, LOGL_INFO, "Sending reset on BVCI 0\n"); + bssgp_tx_bvc_reset(bctx, 0, BSSGP_CAUSE_OML_INTERV); + osmo_timer_schedule(&bvc_timer, 1, 0); + return; + } + + if (!bvc_reset) { + LOGP(DBSSGP, LOGL_INFO, "Sending reset on BVCI %d\n", + bctx->bvci); + bssgp_tx_bvc_reset(bctx, bctx->bvci, BSSGP_CAUSE_OML_INTERV); + osmo_timer_schedule(&bvc_timer, 1, 0); + return; + } + + if (!bvc_unblocked) { + LOGP(DBSSGP, LOGL_INFO, "Sending unblock on BVCI %d\n", + bctx->bvci); + bssgp_tx_bvc_unblock(bctx); + osmo_timer_schedule(&bvc_timer, 1, 0); + return; + } + + LOGP(DBSSGP, LOGL_DEBUG, "Sending flow control info on BVCI %d\n", + bctx->bvci); + gprs_bssgp_tx_fc_bvc(); + osmo_timer_schedule(&bvc_timer, bts->fc_interval, 0); +} + /* create BSSGP/NS layer instances */ int gprs_bssgp_create(uint32_t sgsn_ip, uint16_t sgsn_port, uint16_t nsei, uint16_t nsvci, uint16_t bvci, uint16_t mcc, uint16_t mnc, uint16_t lac, @@ -501,6 +569,9 @@ int gprs_bssgp_create(uint32_t sgsn_ip, uint16_t sgsn_port, uint16_t nsei, // bssgp_tx_bvc_reset(bctx, bctx->bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC); + bvc_timer.cb = bvc_timeout; + + return 0; } @@ -509,6 +580,9 @@ void gprs_bssgp_destroy(void) if (!bssgp_nsi) return; + if (osmo_timer_pending(&bvc_timer)) + osmo_timer_del(&bvc_timer); + osmo_signal_unregister_handler(SS_L_NS, nsvc_signal_cb, NULL); nsvc = NULL; diff --git a/src/gprs_rlcmac.h b/src/gprs_rlcmac.h index a5ac7750..e7a68a4f 100644 --- a/src/gprs_rlcmac.h +++ b/src/gprs_rlcmac.h @@ -62,6 +62,7 @@ struct gprs_rlcmac_trx { }; struct gprs_rlcmac_bts { + uint8_t fc_interval; uint8_t cs1; uint8_t cs2; uint8_t cs3; diff --git a/src/pcu_main.cpp b/src/pcu_main.cpp index 2d7e8ffc..2392152a 100644 --- a/src/pcu_main.cpp +++ b/src/pcu_main.cpp @@ -142,6 +142,7 @@ int main(int argc, char *argv[]) if (!gprs_rlcmac_bts) return -ENOMEM; gprs_rlcmac_bts->initial_cs = 1; + bts->fc_interval = 1; bts->initial_cs = 1; bts->cs1 = 1; bts->t3142 = 20; diff --git a/src/pcu_vty.c b/src/pcu_vty.c index 39a1b722..8d5b47b1 100644 --- a/src/pcu_vty.c +++ b/src/pcu_vty.c @@ -79,6 +79,8 @@ static int config_write_pcu(struct vty *vty) struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; vty_out(vty, "pcu%s", VTY_NEWLINE); + vty_out(vty, " flow-control-interval %d%s", bts->fc_interval, + VTY_NEWLINE); if (bts->force_cs) vty_out(vty, " cs %d%s", bts->initial_cs, VTY_NEWLINE); if (bts->force_llc_lifetime == 0xffff) @@ -106,6 +108,19 @@ DEFUN(cfg_pcu, return CMD_SUCCESS; } +DEFUN(cfg_pcu_fc_interval, + cfg_pcu_fc_interval_cmd, + "flow-control-interval <1..10>", + "Interval between sending subsequent Flow Control PDUs\n" + "Tiem in seconds\n") +{ + struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts; + + bts->fc_interval = atoi(argv[0]); + + return CMD_SUCCESS; +} + DEFUN(cfg_pcu_cs, cfg_pcu_cs_cmd, "cs <1-4>", @@ -242,6 +257,7 @@ int pcu_vty_init(const struct log_info *cat) install_node(&pcu_node, config_write_pcu); install_element(CONFIG_NODE, &cfg_pcu_cmd); install_default(PCU_NODE); + install_element(PCU_NODE, &cfg_pcu_no_two_phase_cmd); install_element(PCU_NODE, &cfg_pcu_cs_cmd); install_element(PCU_NODE, &cfg_pcu_no_cs_cmd); install_element(PCU_NODE, &cfg_pcu_queue_lifetime_cmd); @@ -249,7 +265,7 @@ int pcu_vty_init(const struct log_info *cat) install_element(PCU_NODE, &cfg_pcu_no_queue_lifetime_cmd); install_element(PCU_NODE, &cfg_pcu_alloc_cmd); install_element(PCU_NODE, &cfg_pcu_two_phase_cmd); - install_element(PCU_NODE, &cfg_pcu_no_two_phase_cmd); + install_element(PCU_NODE, &cfg_pcu_fc_interval_cmd); install_element(PCU_NODE, &ournode_end_cmd); return 0;