| /* Ericsson RBS-2xxx specific code */ |
| |
| /* (C) 2011 by Harald Welte <laforge@gnumonks.org> |
| * |
| * All Rights Reserved |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU Affero General Public License as published by |
| * the Free Software Foundation; either version 3 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU Affero General Public License for more details. |
| * |
| * You should have received a copy of the GNU Affero General Public License |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| * |
| */ |
| |
| |
| #include <osmocom/gsm/tlv.h> |
| |
| #include <openbsc/debug.h> |
| #include <openbsc/gsm_data.h> |
| #include <openbsc/abis_om2000.h> |
| #include <openbsc/abis_nm.h> |
| #include <openbsc/e1_input.h> |
| #include <openbsc/signal.h> |
| |
| #include "../libabis/input/lapd.h" |
| |
| static void bootstrap_om_bts(struct gsm_bts *bts) |
| { |
| LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); |
| abis_om2k_tx_start_req(bts, &om2k_mo_cf); |
| /* FIXME */ |
| } |
| |
| static void bootstrap_om_trx(struct gsm_bts_trx *trx) |
| { |
| struct abis_om2k_mo trx_mo = { OM2K_MO_CLS_TRXC, 0, 255, trx->nr }; |
| |
| LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for TRX %u/%u\n", |
| trx->bts->nr, trx->nr); |
| |
| abis_om2k_tx_reset_cmd(trx->bts, &trx_mo); |
| } |
| |
| static int shutdown_om(struct gsm_bts *bts) |
| { |
| /* FIXME */ |
| return 0; |
| } |
| |
| |
| /* Tell LAPD to start start the SAP (send SABM requests) for all signalling |
| * timeslots in this line */ |
| static void start_sabm_in_line(struct e1inp_line *line, int start) |
| { |
| struct e1inp_sign_link *link; |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(line->ts); i++) { |
| struct e1inp_ts *ts = &line->ts[i]; |
| |
| if (ts->type != E1INP_TS_TYPE_SIGN) |
| continue; |
| |
| llist_for_each_entry(link, &ts->sign.sign_links, list) { |
| if (start) |
| lapd_sap_start(ts->driver.dahdi.lapd, link->tei, link->sapi); |
| else |
| lapd_sap_stop(ts->driver.dahdi.lapd, link->tei, link->sapi); |
| } |
| } |
| } |
| |
| /* Callback function to be called every time we receive a signal from INPUT */ |
| static int gbl_sig_cb(unsigned int subsys, unsigned int signal, |
| void *handler_data, void *signal_data) |
| { |
| struct gsm_bts *bts; |
| |
| if (subsys != SS_GLOBAL) |
| return 0; |
| |
| switch (signal) { |
| case S_GLOBAL_BTS_CLOSE_OM: |
| bts = signal_data; |
| if (bts->type == GSM_BTS_TYPE_RBS2000) |
| shutdown_om(signal_data); |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /* Callback function to be called every time we receive a signal from INPUT */ |
| static int inp_sig_cb(unsigned int subsys, unsigned int signal, |
| void *handler_data, void *signal_data) |
| { |
| struct input_signal_data *isd = signal_data; |
| |
| if (subsys != SS_INPUT) |
| return 0; |
| |
| switch (signal) { |
| case S_INP_TEI_UP: |
| switch (isd->link_type) { |
| case E1INP_SIGN_OML: |
| if (isd->trx->bts->type != GSM_BTS_TYPE_RBS2000) |
| break; |
| if (isd->tei == isd->trx->bts->oml_tei) |
| bootstrap_om_bts(isd->trx->bts); |
| else |
| bootstrap_om_trx(isd->trx); |
| break; |
| } |
| break; |
| case S_INP_LINE_INIT: |
| /* Right now Ericsson RBS are only supported on DAHDI */ |
| if (strcasecmp(isd->line->driver->name, "DAHDI")) |
| break; |
| start_sabm_in_line(isd->line, 1); |
| break; |
| case S_INP_LINE_ALARM: |
| if (strcasecmp(isd->line->driver->name, "DAHDI")) |
| break; |
| start_sabm_in_line(isd->line, 0); |
| break; |
| case S_INP_LINE_NOALARM: |
| if (strcasecmp(isd->line->driver->name, "DAHDI")) |
| break; |
| start_sabm_in_line(isd->line, 1); |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static void nm_statechg_evt(unsigned int signal, |
| struct nm_statechg_signal_data *nsd) |
| { |
| struct abis_om2k_mo mo; |
| |
| if (nsd->bts->type != GSM_BTS_TYPE_RBS2000) |
| return; |
| |
| switch (nsd->om2k_mo->class) { |
| case OM2K_MO_CLS_CF: |
| if (nsd->new_state->operational != NM_OPSTATE_ENABLED || |
| nsd->new_state->availability != OM2K_MO_S_STARTED) |
| break; |
| /* CF has started, we can trigger IS and TF start */ |
| abis_om2k_tx_connect_cmd(nsd->bts, &om2k_mo_is); |
| abis_om2k_tx_connect_cmd(nsd->bts, &om2k_mo_tf); |
| break; |
| case OM2K_MO_CLS_IS: |
| if (nsd->new_state->availability == OM2K_MO_S_ENABLED) { |
| /* IS is enabled, we can proceed with TRXC/RX/TX/TS */ |
| break; |
| } |
| if (nsd->new_state->operational != NM_OPSTATE_ENABLED) |
| break; |
| /* IS has started, we can configure + enable it */ |
| abis_om2k_tx_is_conf_req(nsd->bts); |
| break; |
| case OM2K_MO_CLS_TF: |
| if (nsd->new_state->operational != NM_OPSTATE_ENABLED || |
| nsd->new_state->availability == OM2K_MO_S_DISABLED) |
| break; |
| if (nsd->new_state->availability == OM2K_MO_S_STARTED) { |
| /* TF has started, configure + enable it */ |
| abis_om2k_tx_is_conf_req(nsd->bts); |
| } |
| break; |
| case OM2K_MO_CLS_TRXC: |
| if (nsd->new_state->availability != OM2K_MO_S_STARTED) |
| break; |
| /* TRXC is started, connect the TX and RX objects */ |
| memcpy(&mo, nsd->om2k_mo, sizeof(mo)); |
| mo.class = OM2K_MO_CLS_TX; |
| abis_om2k_tx_connect_cmd(nsd->bts, &mo); |
| mo.class = OM2K_MO_CLS_RX; |
| abis_om2k_tx_connect_cmd(nsd->bts, &mo); |
| break; |
| case OM2K_MO_CLS_RX: |
| if (nsd->new_state->operational != NM_OPSTATE_ENABLED || |
| nsd->new_state->availability != OM2K_MO_S_STARTED) |
| break; |
| /* RX is started, configure + enable it */ |
| abis_om2k_tx_rx_conf_req(nsd->obj); |
| break; |
| case OM2K_MO_CLS_TX: |
| if (nsd->new_state->operational != NM_OPSTATE_ENABLED || |
| nsd->new_state->availability != OM2K_MO_S_STARTED) |
| break; |
| /* RX is started, configure + enable it */ |
| abis_om2k_tx_tx_conf_req(nsd->obj); |
| break; |
| } |
| } |
| |
| static void nm_conf_res(struct nm_om2k_signal_data *nsd) |
| { |
| switch (nsd->om2k_mo->class) { |
| case OM2K_MO_CLS_IS: |
| case OM2K_MO_CLS_TF: |
| case OM2K_MO_CLS_RX: |
| case OM2K_MO_CLS_TX: |
| /* If configuration was a success, enable it */ |
| abis_om2k_tx_enable_req(nsd->bts, nsd->om2k_mo); |
| break; |
| } |
| } |
| |
| static int nm_sig_cb(unsigned int subsys, unsigned int signal, |
| void *handler_data, void *signal_data) |
| { |
| if (subsys != SS_NM) |
| return 0; |
| |
| switch (signal) { |
| case S_NM_STATECHG_OPER: |
| case S_NM_STATECHG_ADM: |
| nm_statechg_evt(signal, signal_data); |
| break; |
| case S_NM_OM2K_CONF_RES: |
| nm_conf_res(signal_data); |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static void config_write_bts(struct vty *vty, struct gsm_bts *bts) |
| { |
| abis_om2k_config_write_bts(vty, bts); |
| } |
| |
| static int bts_model_rbs2k_start(struct gsm_network *net); |
| |
| static struct gsm_bts_model model_rbs2k = { |
| .type = GSM_BTS_TYPE_RBS2000, |
| .name = "rbs2000", |
| .start = bts_model_rbs2k_start, |
| .oml_rcvmsg = &abis_om2k_rcvmsg, |
| .config_write_bts = &config_write_bts, |
| }; |
| |
| static int bts_model_rbs2k_start(struct gsm_network *net) |
| { |
| model_rbs2k.features.data = &model_rbs2k._features_data[0]; |
| model_rbs2k.features.data_len = sizeof(model_rbs2k._features_data); |
| |
| gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HOPPING); |
| gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HSCSD); |
| |
| osmo_signal_register_handler(SS_INPUT, inp_sig_cb, NULL); |
| osmo_signal_register_handler(SS_GLOBAL, gbl_sig_cb, NULL); |
| osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); |
| |
| return 0; |
| } |
| |
| int bts_model_rbs2k_init(void) |
| { |
| return gsm_bts_model_register(&model_rbs2k); |
| } |