From fd355a3c6feccca5b774c0b3291a6066d0459067 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 4 Mar 2011 13:41:31 +0100 Subject: [HSL] initial support for the HSL 2.75G Femtocell The HSL Femtocell seems to be a poor man implementation of the ip.access Abis/IP protocol, but cutting corners wherever possible. We try to workaround those corners wherever possible... --- openbsc/include/openbsc/gsm_data.h | 4 + openbsc/src/libabis/Makefile.am | 1 + openbsc/src/libabis/input/hsl.c | 457 ++++++++++++++++++++++++++++++++ openbsc/src/libbsc/Makefile.am | 6 +- openbsc/src/libbsc/abis_nm.c | 6 + openbsc/src/libbsc/abis_rsl.c | 12 +- openbsc/src/libbsc/bsc_init.c | 36 ++- openbsc/src/libbsc/bsc_vty.c | 30 ++- openbsc/src/libbsc/bts_hsl_femtocell.c | 146 ++++++++++ openbsc/src/libbsc/system_information.c | 14 +- openbsc/src/libcommon/gsm_data.c | 3 + openbsc/src/osmo-nitb/bsc_hack.c | 2 + 12 files changed, 706 insertions(+), 11 deletions(-) create mode 100644 openbsc/src/libabis/input/hsl.c create mode 100644 openbsc/src/libbsc/bts_hsl_femtocell.c diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h index 03e7a7c83..ae448c4c1 100644 --- a/openbsc/include/openbsc/gsm_data.h +++ b/openbsc/include/openbsc/gsm_data.h @@ -424,6 +424,7 @@ enum gsm_bts_type { GSM_BTS_TYPE_BS11, GSM_BTS_TYPE_NANOBTS, GSM_BTS_TYPE_RBS2000, + GSM_BTS_TYPE_HSL_FEMTO, }; struct vty; @@ -600,6 +601,9 @@ struct gsm_bts { struct llist_head conn_groups; } con; } rbs2000; + struct { + unsigned long serno; + } hsl; }; /* Not entirely sure how ip.access specific this is */ diff --git a/openbsc/src/libabis/Makefile.am b/openbsc/src/libabis/Makefile.am index 7f5ac47cc..0df7b5a4a 100644 --- a/openbsc/src/libabis/Makefile.am +++ b/openbsc/src/libabis/Makefile.am @@ -7,6 +7,7 @@ noinst_LIBRARIES = libabis.a libabis_a_SOURCES = e1_input.c e1_input_vty.c \ input/misdn.c \ input/ipaccess.c \ + input/hsl.c \ input/dahdi.c \ input/lapd.c diff --git a/openbsc/src/libabis/input/hsl.c b/openbsc/src/libabis/input/hsl.c new file mode 100644 index 000000000..12e974b34 --- /dev/null +++ b/openbsc/src/libabis/input/hsl.c @@ -0,0 +1,457 @@ +/* OpenBSC Abis input driver for HSL Femto */ + +/* (C) 2011 by Harald Welte + * (C) 2011 by On-Waves + * + * 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 . + * + */ + +/* HSL uses a much more primitive/simplified version of the IPA multiplex. + * + * They have taken out the nice parts like the ID_GET / ID_RESP for resolving + * the UNIT ID, as well as the keepalive ping/pong messages. Furthermore, the + * Stream Identifiers are fixed on the BTS side (RSL always 0, OML always 0xff) + * and both OML+RSL share a single TCP connection. + * + * Other oddities include the encapsulation of BSSGP messages in the L3_INFO IE + * of RSL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HSL_TCP_PORT 2500 +#define HSL_PROTO_DEBUG 0xdd + +#define PRIV_OML 1 +#define PRIV_RSL 2 + +/* data structure for one E1 interface with A-bis */ +struct hsl_e1_handle { + struct bsc_fd listen_fd; + struct gsm_network *gsmnet; +}; + +static struct hsl_e1_handle *e1h; + + +#define TS1_ALLOC_SIZE 900 + +#define OML_UP 0x0001 +#define RSL_UP 0x0002 + +int hsl_drop_oml(struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + struct e1inp_ts *ts; + struct e1inp_line *line; + struct bsc_fd *bfd; + + if (!bts || !bts->oml_link) + return -1; + + /* send OML down */ + ts = bts->oml_link->ts; + line = ts->line; + e1inp_event(ts, S_INP_TEI_DN, bts->oml_link->tei, bts->oml_link->sapi); + + bfd = &ts->driver.ipaccess.fd; + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + + /* clean up OML and RSL */ + e1inp_sign_link_destroy(bts->oml_link); + bts->oml_link = NULL; + e1inp_sign_link_destroy(bts->c0->rsl_link); + bts->c0->rsl_link = NULL; + bts->ip_access.flags = 0; + + /* kill the E1 line now... as we have no one left to use it */ + talloc_free(line); + + return -1; +} + +static int hsl_drop_ts_fd(struct e1inp_ts *ts, struct bsc_fd *bfd) +{ + struct e1inp_sign_link *link, *link2; + int bts_nr = -1; + + llist_for_each_entry_safe(link, link2, &ts->sign.sign_links, list) { + bts_nr = link->trx->bts->bts_nr; + e1inp_sign_link_destroy(link); + } + + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + + talloc_free(ts->line); + + return bts_nr; +} + +struct gsm_bts *find_bts_by_serno(struct gsm_network *net, unsigned long serno) +{ + struct gsm_bts *bts; + + llist_for_each_entry(bts, &net->bts_list, list) { + if (bts->type != GSM_BTS_TYPE_HSL_FEMTO) + continue; + + if (serno == bts->hsl.serno) + return bts; + } + + return NULL; +} + + +static int process_hsl_rsl(struct msgb *msg, struct e1inp_line *line) +{ + char serno_buf[16]; + uint8_t serno_len; + unsigned long serno; + struct gsm_bts *bts; + + switch (msg->l2h[1]) { + case 0x80: + /*, contains Serial Number + SW version */ + if (msg->l2h[2] != 0xc0) + break; + serno_len = msg->l2h[3]; + if (serno_len > sizeof(serno_buf)-1) + serno_len = sizeof(serno_buf)-1; + memcpy(serno_buf, msg->l2h+4, serno_len); + serno_buf[serno_len] = '\0'; + serno = strtoul(serno_buf, NULL, 10); + bts = find_bts_by_serno(e1h->gsmnet, serno); + if (!bts) { + LOGP(DINP, LOGL_ERROR, "Unable to find BTS config for " + "serial number %lu(%s)\n", serno, serno_buf); + return -EIO; + } + + DEBUGP(DINP, "Identified HSL BTS Serial Number %lu\n", serno); + + /* we shouldn't hardcode it, but HSL femto also hardcodes it... */ + bts->oml_tei = 255; + bts->c0->rsl_tei = 0; + bts->oml_link = e1inp_sign_link_create(&line->ts[PRIV_OML - 1], + E1INP_SIGN_OML, bts->c0, + bts->oml_tei, 0); + bts->c0->rsl_link = e1inp_sign_link_create(&line->ts[PRIV_OML - 1], + E1INP_SIGN_RSL, bts->c0, + bts->c0->rsl_tei, 0); + e1inp_event(&line->ts[PRIV_OML-1], S_INP_TEI_UP, 255, 0); + e1inp_event(&line->ts[PRIV_OML-1], S_INP_TEI_UP, 0, 0); + bts->ip_access.flags |= OML_UP; + bts->ip_access.flags |= (RSL_UP << 0); + msgb_free(msg); + return 1; /* == we have taken over the msg */ + case 0x82: + /* FIXME: do something with BSSGP, i.e. forward it over + * NSIP to OsmoSGSN */ + return 0; + } + return 0; +} + +static int handle_ts1_read(struct bsc_fd *bfd) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; + struct e1inp_sign_link *link; + struct msgb *msg; + struct ipaccess_head *hh; + int ret = 0, error; + + msg = ipaccess_read_msg(bfd, &error); + if (!msg) { + if (error == 0) { + int ret = hsl_drop_ts_fd(e1i_ts, bfd); + if (ret >= 0) + LOGP(DINP, LOGL_NOTICE, "BTS %u disappeared, dead socket\n", + ret); + else + LOGP(DINP, LOGL_NOTICE, "unknown BTS disappeared, dead socket\n"); + } + return error; + } + + DEBUGP(DMI, "RX %u: %s\n", ts_nr, hexdump(msgb_l2(msg), msgb_l2len(msg))); + + hh = (struct ipaccess_head *) msg->data; + if (hh->proto == HSL_PROTO_DEBUG) { + LOGP(DINP, LOGL_NOTICE, "HSL debug: %s\n", msg->data + sizeof(*hh)); + msgb_free(msg); + return ret; + } + + /* HSL proprietary RSL extension */ + if (hh->proto == 0 && msg->l2h[0] == 0x80) { + ret = process_hsl_rsl(msg, line); + if (ret < 0) { + /* FIXME: close connection */ + hsl_drop_ts_fd(e1i_ts, bfd); + return ret; + } else if (ret == 1) + return 0; + /* else: continue... */ + } + + /* HSL for whatever reason chose to use 0x81 instead of 0x80 for FOM */ + if (hh->proto == 255 && msg->l2h[0] == (ABIS_OM_MDISC_FOM | 0x01)) + msg->l2h[0] = ABIS_OM_MDISC_FOM; + + link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0); + if (!link) { + LOGP(DINP, LOGL_ERROR, "no matching signalling link for " + "hh->proto=0x%02x\n", hh->proto); + msgb_free(msg); + return -EIO; + } + msg->trx = link->trx; + + switch (link->type) { + case E1INP_SIGN_RSL: + if (!(msg->trx->bts->ip_access.flags & (RSL_UP << msg->trx->nr))) { + e1inp_event(e1i_ts, S_INP_TEI_UP, link->tei, link->sapi); + msg->trx->bts->ip_access.flags |= (RSL_UP << msg->trx->nr); + } + ret = abis_rsl_rcvmsg(msg); + break; + case E1INP_SIGN_OML: + if (!(msg->trx->bts->ip_access.flags & OML_UP)) { + e1inp_event(e1i_ts, S_INP_TEI_UP, link->tei, link->sapi); + msg->trx->bts->ip_access.flags |= OML_UP; + } + ret = abis_nm_rcvmsg(msg); + break; + default: + LOGP(DINP, LOGL_NOTICE, "Unknown HSL protocol class 0x%02x\n", hh->proto); + msgb_free(msg); + break; + } + return ret; +} + +static int ts_want_write(struct e1inp_ts *e1i_ts) +{ + e1i_ts->driver.ipaccess.fd.when |= BSC_FD_WRITE; + + return 0; +} + +static void timeout_ts1_write(void *data) +{ + struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data; + + /* trigger write of ts1, due to tx delay timer */ + ts_want_write(e1i_ts); +} + +static int handle_ts1_write(struct bsc_fd *bfd) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; + struct e1inp_sign_link *sign_link; + struct msgb *msg; + u_int8_t proto; + int ret; + + bfd->when &= ~BSC_FD_WRITE; + + /* get the next msg for this timeslot */ + msg = e1inp_tx_ts(e1i_ts, &sign_link); + if (!msg) { + /* no message after tx delay timer */ + return 0; + } + + switch (sign_link->type) { + case E1INP_SIGN_OML: + proto = IPAC_PROTO_OML; + /* HSL uses 0x81 for FOM for some reason */ + if (msg->data[0] == ABIS_OM_MDISC_FOM) + msg->data[0] = ABIS_OM_MDISC_FOM | 0x01; + break; + case E1INP_SIGN_RSL: + proto = IPAC_PROTO_RSL; + break; + default: + msgb_free(msg); + bfd->when |= BSC_FD_WRITE; /* come back for more msg */ + return -EINVAL; + } + + msg->l2h = msg->data; + ipaccess_prepend_header(msg, sign_link->tei); + + DEBUGP(DMI, "TX %u: %s\n", ts_nr, hexdump(msg->l2h, msgb_l2len(msg))); + + ret = send(bfd->fd, msg->data, msg->len, 0); + msgb_free(msg); + + /* set tx delay timer for next event */ + e1i_ts->sign.tx_timer.cb = timeout_ts1_write; + e1i_ts->sign.tx_timer.data = e1i_ts; + + /* Reducing this might break the nanoBTS 900 init. */ + bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, e1i_ts->sign.delay); + + return ret; +} + +/* callback from select.c in case one of the fd's can be read/written */ +static int hsl_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + unsigned int idx = ts_nr-1; + struct e1inp_ts *e1i_ts; + int rc = 0; + + /* In case of early RSL we might not yet have a line */ + + if (line) + e1i_ts = &line->ts[idx]; + + if (!line || e1i_ts->type == E1INP_TS_TYPE_SIGN) { + if (what & BSC_FD_READ) + rc = handle_ts1_read(bfd); + if (what & BSC_FD_WRITE) + rc = handle_ts1_write(bfd); + } else + LOGP(DINP, LOGL_ERROR, "unknown E1 TS type %u\n", e1i_ts->type); + + return rc; +} + +struct e1inp_driver hsl_driver = { + .name = "HSL", + .want_write = ts_want_write, + .default_delay = 0, +}; + +/* callback of the OML listening filedescriptor */ +static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what) +{ + int ret; + int idx = 0; + int i; + struct e1inp_line *line; + struct e1inp_ts *e1i_ts; + struct bsc_fd *bfd; + struct sockaddr_in sa; + socklen_t sa_len = sizeof(sa); + + if (!(what & BSC_FD_READ)) + return 0; + + ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len); + if (ret < 0) { + perror("accept"); + return ret; + } + LOGP(DINP, LOGL_NOTICE, "accept()ed new HSL link from %s\n", + inet_ntoa(sa.sin_addr)); + + line = talloc_zero(tall_bsc_ctx, struct e1inp_line); + if (!line) { + close(ret); + return -ENOMEM; + } + line->driver = &hsl_driver; + //line->driver_data = e1h; + /* create virrtual E1 timeslots for signalling */ + e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN); + + /* initialize the fds */ + for (i = 0; i < ARRAY_SIZE(line->ts); ++i) + line->ts[i].driver.ipaccess.fd.fd = -1; + + e1i_ts = &line->ts[idx]; + + bfd = &e1i_ts->driver.ipaccess.fd; + bfd->fd = ret; + bfd->data = line; + bfd->priv_nr = PRIV_OML; + bfd->cb = hsl_fd_cb; + bfd->when = BSC_FD_READ; + ret = bsc_register_fd(bfd); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not register FD\n"); + close(bfd->fd); + talloc_free(line); + return ret; + } + + return ret; + //return e1inp_line_register(line); +} + +int hsl_setup(struct gsm_network *gsmnet) +{ + int ret; + + /* register the driver with the core */ + /* FIXME: do this in the plugin initializer function */ + ret = e1inp_driver_register(&hsl_driver); + if (ret) + return ret; + + e1h = talloc_zero(tall_bsc_ctx, struct hsl_e1_handle); + if (!e1h) + return -ENOMEM; + + e1h->gsmnet = gsmnet; + + /* Listen for connections */ + ret = make_sock(&e1h->listen_fd, IPPROTO_TCP, 0, HSL_TCP_PORT, + listen_fd_cb); + if (ret < 0) + return ret; + + return 0; +} diff --git a/openbsc/src/libbsc/Makefile.am b/openbsc/src/libbsc/Makefile.am index 91be7fa62..de7692950 100644 --- a/openbsc/src/libbsc/Makefile.am +++ b/openbsc/src/libbsc/Makefile.am @@ -8,7 +8,11 @@ libbsc_a_SOURCES = abis_nm.c abis_nm_vty.c \ abis_om2000.c abis_om2000_vty.c \ abis_rsl.c bsc_rll.c \ paging.c \ - bts_ericsson_rbs2000.c bts_ipaccess_nanobts.c bts_siemens_bs11.c bts_unknown.c \ + bts_ericsson_rbs2000.c \ + bts_ipaccess_nanobts.c \ + bts_siemens_bs11.c \ + bts_hsl_femtocell.c \ + bts_unknown.c \ chan_alloc.c \ gsm_subscriber_base.c \ handover_decision.c handover_logic.c meas_rep.c \ diff --git a/openbsc/src/libbsc/abis_nm.c b/openbsc/src/libbsc/abis_nm.c index 45db8bac5..0e7fc8d8c 100644 --- a/openbsc/src/libbsc/abis_nm.c +++ b/openbsc/src/libbsc/abis_nm.c @@ -1080,6 +1080,12 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb) case NM_MT_IPACC_RESTART_NACK: dispatch_signal(SS_NM, S_NM_IPACC_RESTART_NACK, NULL); break; + case NM_MT_SET_BTS_ATTR_ACK: + /* The HSL wants an OPSTART _after_ the SI has been set */ + if (mb->trx->bts->type == GSM_BTS_TYPE_HSL_FEMTO) { + abis_nm_opstart(mb->trx->bts, NM_OC_BTS, 255, 255, 255); + } + break; } abis_nm_queue_send_next(mb->trx->bts); diff --git a/openbsc/src/libbsc/abis_rsl.c b/openbsc/src/libbsc/abis_rsl.c index 07a7dc68c..58e2a7cd7 100644 --- a/openbsc/src/libbsc/abis_rsl.c +++ b/openbsc/src/libbsc/abis_rsl.c @@ -636,11 +636,14 @@ static void error_timeout_cb(void *data) rsl_lchan_set_state(lchan, LCHAN_S_NONE); } +static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan); + /* Chapter 8.4.14 / 4.7: Tell BTS to release the radio channel */ static int rsl_rf_chan_release(struct gsm_lchan *lchan, int error) { struct abis_rsl_dchan_hdr *dh; struct msgb *msg; + int rc; if (lchan->state == LCHAN_S_REL_ERR) { LOGP(DRSL, LOGL_NOTICE, "%s is in error state not sending release.\n", @@ -671,8 +674,15 @@ static int rsl_rf_chan_release(struct gsm_lchan *lchan, int error) msg->trx->bts->network->T3111 + 2, 0); } + rc = abis_rsl_sendmsg(msg); + /* BTS will respond by RF CHAN REL ACK */ - return abis_rsl_sendmsg(msg); + + /* The HSL Femto seems to 'forget' sending a REL ACK for TS1...TS7 */ + if (lchan->ts->trx->bts->type == GSM_BTS_TYPE_HSL_FEMTO && lchan->ts->nr != 0) + rc = rsl_rx_rf_chan_rel_ack(lchan); + + return rc; } static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan) diff --git a/openbsc/src/libbsc/bsc_init.c b/openbsc/src/libbsc/bsc_init.c index 752056c37..0072bb6f8 100644 --- a/openbsc/src/libbsc/bsc_init.c +++ b/openbsc/src/libbsc/bsc_init.c @@ -92,13 +92,14 @@ int bsc_shutdown_net(struct gsm_network *net) static int generate_and_rsl_si(struct gsm_bts_trx *trx, enum osmo_sysinfo_type i) { struct gsm_bts *bts = trx->bts; - int rc; + int si_len, rc, j; /* Only generate SI if this SI is not in "static" (user-defined) mode */ if (!(bts->si_mode_static & (1 << i))) { rc = gsm_generate_si(bts, i); if (rc < 0) return rc; + si_len = rc; } DEBUGP(DRR, "SI%s: %s\n", gsm_sitype_name(i), @@ -109,8 +110,25 @@ static int generate_and_rsl_si(struct gsm_bts_trx *trx, enum osmo_sysinfo_type i case SYSINFO_TYPE_5bis: case SYSINFO_TYPE_5ter: case SYSINFO_TYPE_6: - rc = rsl_sacch_filling(trx, gsm_sitype2rsl(i), - GSM_BTS_SI(bts, i), rc); + if (trx->bts->type == GSM_BTS_TYPE_HSL_FEMTO) { + /* HSL has mistaken SACCH INFO MODIFY for SACCH FILLING, + * so we need a special workaround here */ + /* This assumes a combined BCCH and TCH on TS1...7 */ + for (j = 0; j < 4; j++) + rsl_sacch_info_modify(&trx->ts[0].lchan[j], + gsm_sitype2rsl(i), + GSM_BTS_SI(bts, i), si_len); + for (j = 1; j < 8; j++) { + rsl_sacch_info_modify(&trx->ts[j].lchan[0], + gsm_sitype2rsl(i), + GSM_BTS_SI(bts, i), si_len); + rsl_sacch_info_modify(&trx->ts[j].lchan[1], + gsm_sitype2rsl(i), + GSM_BTS_SI(bts, i), si_len); + } + } else + rc = rsl_sacch_filling(trx, gsm_sitype2rsl(i), + GSM_BTS_SI(bts, i), rc); break; default: rc = rsl_bcch_info(trx, gsm_sitype2rsl(i), @@ -423,9 +441,16 @@ int bsc_bootstrap_network(int (*mncc_recv)(struct gsm_network *, struct msgb *), register_signal_handler(SS_INPUT, inp_sig_cb, NULL); llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) { - bootstrap_bts(bts); - if (!is_ipaccess_bts(bts)) + rc = bootstrap_bts(bts); + + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + case GSM_BTS_TYPE_HSL_FEMTO: + break; + default: rc = e1_reconfig_bts(bts); + break; + } if (rc < 0) { fprintf(stderr, "Error in E1 input driver setup\n"); @@ -435,6 +460,7 @@ int bsc_bootstrap_network(int (*mncc_recv)(struct gsm_network *, struct msgb *), /* initialize nanoBTS support omce */ rc = ipaccess_setup(bsc_gsmnet); + rc = hsl_setup(bsc_gsmnet); return 0; } diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c index ed45afd4d..57a0c2005 100644 --- a/openbsc/src/libbsc/bsc_vty.c +++ b/openbsc/src/libbsc/bsc_vty.c @@ -253,6 +253,8 @@ static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts) vty_out(vty, " Unit ID: %u/%u/0, OML Stream ID 0x%02x%s", bts->ip_access.site_id, bts->ip_access.bts_id, bts->oml_tei, VTY_NEWLINE); + else if (bts->type == GSM_BTS_TYPE_HSL_FEMTO) + vty_out(vty, " Serial Number: %lu%s", bts->hsl.serno, VTY_NEWLINE); vty_out(vty, " NM State: "); net_dump_nmstate(vty, &bts->nm_state); vty_out(vty, " Site Mgr NM State: "); @@ -497,13 +499,19 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) VTY_NEWLINE); } } - if (is_ipaccess_bts(bts)) { + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: vty_out(vty, " ip.access unit_id %u %u%s", bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE); vty_out(vty, " oml ip.access stream_id %u%s", bts->oml_tei, VTY_NEWLINE); - } else { + break; + case GSM_BTS_TYPE_HSL_FEMTO: + vty_out(vty, " hsl serial-number %lu%s", bts->hsl.serno, VTY_NEWLINE); + break; + default: config_write_e1_link(vty, &bts->oml_e1_link, " oml "); vty_out(vty, " oml e1 tei %u%s", bts->oml_tei, VTY_NEWLINE); + break; } /* if we have a limit, write it */ @@ -1584,6 +1592,23 @@ DEFUN(cfg_bts_unit_id, return CMD_SUCCESS; } +DEFUN(cfg_bts_serno, + cfg_bts_serno_cmd, + "hsl serial-number STRING", + "Set the HSL Serial Number of this BTS\n") +{ + struct gsm_bts *bts = vty->index; + + if (bts->type != GSM_BTS_TYPE_HSL_FEMTO) { + vty_out(vty, "%% BTS is not of HSL type%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bts->hsl.serno = strtoul(argv[0], NULL, 10); + + return CMD_SUCCESS; +} + #define OML_STR "Organization & Maintenance Link\n" #define IPA_STR "ip.access Specific Options\n" @@ -2696,6 +2721,7 @@ int bsc_vty_init(void) install_element(BTS_NODE, &cfg_bts_tsc_cmd); install_element(BTS_NODE, &cfg_bts_bsic_cmd); install_element(BTS_NODE, &cfg_bts_unit_id_cmd); + install_element(BTS_NODE, &cfg_bts_serno_cmd); install_element(BTS_NODE, &cfg_bts_stream_id_cmd); install_element(BTS_NODE, &cfg_bts_oml_e1_cmd); install_element(BTS_NODE, &cfg_bts_oml_e1_tei_cmd); diff --git a/openbsc/src/libbsc/bts_hsl_femtocell.c b/openbsc/src/libbsc/bts_hsl_femtocell.c new file mode 100644 index 000000000..2813195b2 --- /dev/null +++ b/openbsc/src/libbsc/bts_hsl_femtocell.c @@ -0,0 +1,146 @@ +/* OpenBSC support code for HSL Femtocell */ + +/* (C) 2011 by Harald Welte + * (C) 2011 by OnWaves + * + * 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 . + * + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include + +static struct gsm_bts_model model_hslfemto = { + .type = GSM_BTS_TYPE_HSL_FEMTO, + .nm_att_tlvdef = { + .def = { + /* no HSL specific OML attributes that we know of */ + }, + }, +}; + + +static const uint8_t l1_msg[] = { + 0x80, 0x8a, + 0xC4, 0x0b, +}; + +static const uint8_t conn_trau_msg[] = { + 0x80, 0x81, + 0xC1, 16, + 0x02, 0x00, 0x00, 0x00, 0xC0, 0xA8, 0xEA, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t conn_trau_msg2[] = { + 0x80, 0x81, + 0xC1, 16, + 0x02, 0x00, 0xd4, 0x07, 0xC0, 0xA8, 0xEA, 0x01, + 0x38, 0xA4, 0x45, 0x00, 0x04, 0x59, 0x40, 0x00 +}; + +static uint8_t oml_arfcn_bsic[] = { + 0x81, 0x80, 0x00, 10, + NM_MT_SET_BTS_ATTR, NM_OC_BTS, 0xff, 0xff, 0xff, + NM_ATT_BCCH_ARFCN, 0x03, 0x67, + NM_ATT_BSIC, 0x00 +}; + +static inline struct msgb *hsl_alloc_msgb(void) +{ + return msgb_alloc_headroom(1024, 127, "HSL"); +} + +static int hslfemto_bootstrap_om(struct gsm_bts *bts) +{ + struct msgb *msg; + uint8_t *cur; + + msg = hsl_alloc_msgb(); + cur = msgb_put(msg, sizeof(l1_msg)); + memcpy(msg->data, l1_msg, sizeof(l1_msg)); + msg->trx = bts->c0; + abis_rsl_sendmsg(msg); + +#if 1 + msg = hsl_alloc_msgb(); + cur = msgb_put(msg, sizeof(conn_trau_msg)); + memcpy(msg->data, conn_trau_msg, sizeof(conn_trau_msg)); + msg->trx = bts->c0; + abis_rsl_sendmsg(msg); +#endif + msg = hsl_alloc_msgb(); + cur = msgb_put(msg, sizeof(conn_trau_msg2)); + memcpy(msg->data, conn_trau_msg2, sizeof(conn_trau_msg2)); + msg->trx = bts->c0; + abis_rsl_sendmsg(msg); + + *((uint16_t *)oml_arfcn_bsic+10) = htons(bts->c0->arfcn); + oml_arfcn_bsic[13] = bts->bsic; + + msg = hsl_alloc_msgb(); + cur = msgb_put(msg, sizeof(oml_arfcn_bsic)); + memcpy(msg->data, oml_arfcn_bsic, sizeof(oml_arfcn_bsic)); + msg->trx = bts->c0; + _abis_nm_sendmsg(msg, 0); + + /* Delay the OPSTART until after SI have been set via RSL */ + //abis_nm_opstart(bts, NM_OC_BTS, 255, 255, 255); + + 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: + hslfemto_bootstrap_om(isd->trx->bts); + break; + } + } + + return 0; +} + +int bts_model_hslfemto_init(void) +{ + model_hslfemto.features.data = &model_hslfemto._features_data[0]; + model_hslfemto.features.data_len = sizeof(model_hslfemto._features_data); + + gsm_btsmodel_set_feature(&model_hslfemto, BTS_FEAT_GPRS); + gsm_btsmodel_set_feature(&model_hslfemto, BTS_FEAT_EGPRS); + + register_signal_handler(SS_INPUT, inp_sig_cb, NULL); + + return gsm_bts_model_register(&model_hslfemto); +} diff --git a/openbsc/src/libbsc/system_information.c b/openbsc/src/libbsc/system_information.c index 8a99c565e..dc719388b 100644 --- a/openbsc/src/libbsc/system_information.c +++ b/openbsc/src/libbsc/system_information.c @@ -370,9 +370,14 @@ static int generate_si5(u_int8_t *output, struct gsm_bts *bts) memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); /* ip.access nanoBTS needs l2_plen!! */ - if (is_ipaccess_bts(bts)) { + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + case GSM_BTS_TYPE_HSL_FEMTO: *output++ = (l2_plen << 2) | 1; l2_plen++; + break; + default: + break; } si5 = (struct gsm48_system_information_type_5 *) output; @@ -397,9 +402,14 @@ static int generate_si6(u_int8_t *output, struct gsm_bts *bts) memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); /* ip.access nanoBTS needs l2_plen!! */ - if (is_ipaccess_bts(bts)) { + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + case GSM_BTS_TYPE_HSL_FEMTO: *output++ = (l2_plen << 2) | 1; l2_plen++; + break; + default: + break; } si6 = (struct gsm48_system_information_type_6 *) output; diff --git a/openbsc/src/libcommon/gsm_data.c b/openbsc/src/libcommon/gsm_data.c index f181fd162..b2c52e4b1 100644 --- a/openbsc/src/libcommon/gsm_data.c +++ b/openbsc/src/libcommon/gsm_data.c @@ -416,6 +416,7 @@ static const struct value_string bts_types[] = { { GSM_BTS_TYPE_BS11, "bs11" }, { GSM_BTS_TYPE_NANOBTS, "nanobts" }, { GSM_BTS_TYPE_RBS2000, "rbs2000" }, + { GSM_BTS_TYPE_HSL_FEMTO, "hsl_femto" }, { 0, NULL } }; @@ -571,6 +572,8 @@ int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type) bts->model = model; switch (bts->type) { + case GSM_BTS_TYPE_HSL_FEMTO: + bts->c0->rsl_tei = 0; case GSM_BTS_TYPE_NANOBTS: /* Set the default OML Stream ID to 0xff */ bts->oml_tei = 0xff; diff --git a/openbsc/src/osmo-nitb/bsc_hack.c b/openbsc/src/osmo-nitb/bsc_hack.c index 8c6d30b0d..88814b66d 100644 --- a/openbsc/src/osmo-nitb/bsc_hack.c +++ b/openbsc/src/osmo-nitb/bsc_hack.c @@ -211,6 +211,7 @@ extern int bts_model_unknown_init(void); extern int bts_model_bs11_init(void); extern int bts_model_nanobts_init(void); extern int bts_model_rbs2k_init(void); +extern int bts_model_hslfemto_init(void); void talloc_ctx_init(void); extern enum node_type bsc_vty_go_parent(struct vty *vty); @@ -241,6 +242,7 @@ int main(int argc, char **argv) bts_model_bs11_init(); bts_model_nanobts_init(); bts_model_rbs2k_init(); + bts_model_hslfemto_init(); e1inp_init(); -- cgit v1.2.3