aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/osmo-bts/abis.h68
-rw-r--r--include/osmo-bts/bts.h133
-rw-r--r--include/osmo-bts/oml.h9
-rw-r--r--include/osmo-bts/rsl.h12
-rw-r--r--include/osmo-bts/rtp.h47
-rw-r--r--include/osmo-bts/support.h30
-rw-r--r--src/common/abis.c488
-rw-r--r--src/common/bts.c376
-rw-r--r--src/common/l1ctl.c305
-rw-r--r--src/common/oml.c549
-rw-r--r--src/common/rsl.c787
-rw-r--r--src/common/rtp.c517
-rw-r--r--src/common/support.c83
-rw-r--r--src/osmo-bts-bb/main.c216
14 files changed, 3620 insertions, 0 deletions
diff --git a/include/osmo-bts/abis.h b/include/osmo-bts/abis.h
new file mode 100644
index 00000000..517c6e72
--- /dev/null
+++ b/include/osmo-bts/abis.h
@@ -0,0 +1,68 @@
+#ifndef _ABIS_H
+#define _ABIS_H
+
+#define IPA_TCP_PORT_OML 3002
+#define IPA_TCP_PORT_RSL 3003
+
+#define OML_RETRY_TIMER 5
+#define OML_PING_TIMER 20
+
+struct ipabis_head {
+ u_int16_t len; /* network byte order */
+ u_int8_t proto;
+ u_int8_t data[0];
+} __attribute__ ((packed));
+
+enum ipabis_proto {
+ IPA_PROTO_RSL = 0x00,
+ IPA_PROTO_IPACCESS = 0xfe,
+ IPA_PROTO_SCCP = 0xfd,
+ IPA_PROTO_OML = 0xff,
+};
+
+enum ipabis_msgtype {
+ IPA_MSGT_PING = 0x00,
+ IPA_MSGT_PONG = 0x01,
+ IPA_MSGT_ID_GET = 0x04,
+ IPA_MSGT_ID_RESP = 0x05,
+ IPA_MSGT_ID_ACK = 0x06,
+};
+
+enum ipabis_id_tags {
+ IPA_IDTAG_SERNR = 0x00,
+ IPA_IDTAG_UNITNAME = 0x01,
+ IPA_IDTAG_LOCATION1 = 0x02,
+ IPA_IDTAG_LOCATION2 = 0x03,
+ IPA_IDTAG_EQUIPVERS = 0x04,
+ IPA_IDTAG_SWVERSION = 0x05,
+ IPA_IDTAG_IPADDR = 0x06,
+ IPA_IDTAG_MACADDR = 0x07,
+ IPA_IDTAG_UNIT = 0x08,
+};
+
+struct ipabis_link {
+ int state;
+ struct osmocom_bts *bts; /* set, if OML link */
+ struct osmobts_trx *trx; /* set, if RSL link */
+ struct bsc_fd bfd;
+ struct timer_list timer;
+ struct msgb *rx_msg;
+ struct llist_head tx_queue;
+ int ping, pong, id_resp;
+ uint32_t ip;
+};
+
+enum {
+ LINK_STATE_IDLE = 0,
+ LINK_STATE_RETRYING,
+ LINK_STATE_CONNECTING,
+ LINK_STATE_CONNECT,
+};
+
+int abis_tx(struct ipabis_link *link, struct msgb *msg);
+struct msgb *abis_msgb_alloc(int headroom);
+void abis_push_ipa(struct msgb *msg, uint8_t proto);
+int abis_open(struct ipabis_link *link, uint32_t ip);
+void abis_close(struct ipabis_link *link);
+
+#endif /* _ABIS_H */
diff --git a/include/osmo-bts/bts.h b/include/osmo-bts/bts.h
new file mode 100644
index 00000000..3e37e0bf
--- /dev/null
+++ b/include/osmo-bts/bts.h
@@ -0,0 +1,133 @@
+#ifndef _BTS_H
+#define _BTS_H
+
+#define BTS_SI_NUM 23 /* MUAR match the entries in BTS_SI_LIST */
+
+#define BTS_SI_LIST { \
+ RSL_SYSTEM_INFO_8, \
+ RSL_SYSTEM_INFO_1, \
+ RSL_SYSTEM_INFO_2, \
+ RSL_SYSTEM_INFO_3, \
+ RSL_SYSTEM_INFO_4, \
+ RSL_SYSTEM_INFO_5, \
+ RSL_SYSTEM_INFO_6, \
+ RSL_SYSTEM_INFO_7, \
+ RSL_SYSTEM_INFO_16, \
+ RSL_SYSTEM_INFO_17, \
+ RSL_SYSTEM_INFO_2bis, \
+ RSL_SYSTEM_INFO_2ter, \
+ RSL_SYSTEM_INFO_5bis, \
+ RSL_SYSTEM_INFO_5ter, \
+ RSL_SYSTEM_INFO_10, \
+ REL_EXT_MEAS_ORDER, \
+ RSL_MEAS_INFO, \
+ RSL_SYSTEM_INFO_13, \
+ RSL_SYSTEM_INFO_2quater, \
+ RSL_SYSTEM_INFO_9, \
+ RSL_SYSTEM_INFO_18, \
+ RSL_SYSTEM_INFO_19, \
+ RSL_SYSTEM_INFO_20, \
+}
+
+#define BTS_SI_NAME char *bts_si_name[] = { \
+ "RSL_SYSTEM_INFO_8", \
+ "RSL_SYSTEM_INFO_1", \
+ "RSL_SYSTEM_INFO_2", \
+ "RSL_SYSTEM_INFO_3", \
+ "RSL_SYSTEM_INFO_4", \
+ "RSL_SYSTEM_INFO_5", \
+ "RSL_SYSTEM_INFO_6", \
+ "RSL_SYSTEM_INFO_7", \
+ "RSL_SYSTEM_INFO_16", \
+ "RSL_SYSTEM_INFO_17", \
+ "RSL_SYSTEM_INFO_2bis", \
+ "RSL_SYSTEM_INFO_2ter", \
+ "RSL_SYSTEM_INFO_5bis", \
+ "RSL_SYSTEM_INFO_5ter", \
+ "RSL_SYSTEM_INFO_10", \
+ "REL_EXT_MEAS_ORDER", \
+ "RSL_MEAS_INFO", \
+ "RSL_SYSTEM_INFO_13", \
+ "RSL_SYSTEM_INFO_2quater", \
+ "RSL_SYSTEM_INFO_9", \
+ "RSL_SYSTEM_INFO_18", \
+ "RSL_SYSTEM_INFO_19", \
+ "RSL_SYSTEM_INFO_20", \
+}
+
+#define BTS_SI_USE 1
+#define BTS_SI_NEW 2
+
+/* store sysinfos of a BTS */
+struct osmobts_sysinfo {
+ uint8_t flags[BTS_SI_NUM];
+ uint8_t si[BTS_SI_NUM][23];
+ struct timer_list timer;
+};
+
+struct osmobts_slot;
+
+/* one physical radio */
+struct osmobts_ms {
+ struct llist_head entry;
+ struct osmobts_trx *trx;
+ struct osmocom_ms ms;
+};
+
+/* one logical channel instance */
+struct osmobts_lchan {
+ struct osmobts_slot *slot;
+ uint8_t lchan_nr;
+ uint8_t chan_nr; /* CBITS+TN */
+ struct osmol2_entity l2_entity;
+ struct osmobts_rtp rtp;
+};
+
+/* one timeslot instance */
+struct osmobts_slot {
+ struct osmobts_trx *trx;
+ uint8_t slot_nr;
+ uint8_t acch_type; /* TS 08.58 9.3.1 (bits 8..4) */
+ uint8_t has_bcch;
+ uint8_t chan_comb;
+ struct osmobts_lchan *lchan[8];
+ struct osmobts_ms *tx_ms, *rx_ms;
+};
+
+/* one TRX instance */
+struct osmobts_trx {
+ struct osmocom_bts *bts;
+ uint8_t trx_nr;
+ struct osmobts_slot slot[8];
+ struct llist_head ms_list;
+ struct ipabis_link link;
+ struct osmobts_sysinfo si;
+ uint8_t rf_red;
+ uint16_t arfcn_list[128];
+ int arfcn_num;
+};
+
+/* the BTS instance */
+struct osmocom_bts {
+ char *id;
+ uint8_t num_trx;
+ struct osmobts_trx *trx[8];
+ struct ipabis_link link;
+ uint8_t max_ta;
+ uint16_t bcch_arfcn;
+ uint8_t bcc, ncc;
+ uint16_t start_time;
+};
+
+struct osmocom_bts *create_bts(uint8_t num_trx, char *id);
+int create_ms(struct osmobts_trx *trx, int maskc, uint8_t *maskv_tx,
+ uint8_t *maskv_rx);
+void destroy_bts(struct osmocom_bts *bts);
+int work_bts(struct osmocom_bts *bts);
+int bts_link_estab(struct osmocom_bts *bts);
+int trx_link_estab(struct osmobts_trx *trx);
+void bts_new_si(void *arg);
+void bts_setup_slot(struct osmobts_slot *slot, uint8_t comb);
+
+#endif /* _BTS_H */
+
diff --git a/include/osmo-bts/oml.h b/include/osmo-bts/oml.h
new file mode 100644
index 00000000..09ad1abd
--- /dev/null
+++ b/include/osmo-bts/oml.h
@@ -0,0 +1,9 @@
+#ifndef _OML_H
+#define _OML_H
+
+int down_oml(struct osmocom_bts *bts, struct msgb *msg);
+int oml_tx_sw_act_rep(struct ipabis_link *link, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr);
+int oml_tx_state_changed(struct ipabis_link *link, uint8_t op_state, uint8_t avail_status, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr);
+
+#endif // _OML_H */
+
diff --git a/include/osmo-bts/rsl.h b/include/osmo-bts/rsl.h
new file mode 100644
index 00000000..3f9c2c9a
--- /dev/null
+++ b/include/osmo-bts/rsl.h
@@ -0,0 +1,12 @@
+#ifndef _RSL_H
+#define _RSL_H
+
+int down_rsl(struct osmobts_trx *trx, struct msgb *msg);
+int rsl_tx_rf_res(struct osmobts_trx *trx);
+int rsl_tx_chan_rqd(struct osmobts_trx *trx);
+int rsl_tx_est_ind(struct osmobts_lchan *lchan, uint8_t link_id, uint8_t *data, int len);
+int rsl_tx_rll(struct msgb *msg, struct osmol2_entity *l2_entity);
+int rsl_tx_ipac_dlcx_ind(struct osmobts_lchan *lchan, uint8_t cause);
+
+#endif // _RSL_H */
+
diff --git a/include/osmo-bts/rtp.h b/include/osmo-bts/rtp.h
new file mode 100644
index 00000000..28800fa0
--- /dev/null
+++ b/include/osmo-bts/rtp.h
@@ -0,0 +1,47 @@
+#ifndef _RTP_H
+#define _RTP_H
+
+struct osmobts_lchan *lchan;
+
+#define RTP_PT_GSM_FULL 3
+#define RTP_PT_GSM_HALF 96
+#define RTP_PT_GSM_EFR 97
+#define RTP_PT_AMR_FULL 98
+#define RTP_PT_AMR_HALF 99
+
+struct osmobts_udp {
+ struct osmobts_rtp *rtp;
+ struct bsc_fd bfd;
+ struct sockaddr_in sin_local, sin_remote;
+ struct llist_head tx_queue;
+};
+
+struct osmobts_rtp {
+ struct osmobts_lchan *lchan;
+ int socket_created;
+ struct osmobts_udp rtp_udp, rtcp_udp;
+ struct llist_head dejitter_queue;
+ int dejitter_num;
+ int dejitter_min;
+ int dejitter_check;
+ struct msgb *last_frame;
+ uint8_t payload_type;
+ uint16_t sequence;
+ uint32_t timestamp;
+ uint32_t ssrc;
+ int voice_req;
+};
+
+int rtp_create_socket(struct osmobts_lchan *lchan, struct osmobts_rtp *rtp);
+int rtp_bind_socket(struct osmobts_rtp *rtp);
+int rtp_connect_socket(struct osmobts_rtp *rtp, uint32_t ip, uint16_t port);
+int rtp_close_socket(struct osmobts_rtp *rtp);
+
+struct gsm_data_frame {
+ uint32_t timestamp;
+ uint8_t payload_type;
+ uint8_t data[0];
+};
+
+#endif /* _RTP_H */
+
diff --git a/include/osmo-bts/support.h b/include/osmo-bts/support.h
new file mode 100644
index 00000000..ed378753
--- /dev/null
+++ b/include/osmo-bts/support.h
@@ -0,0 +1,30 @@
+#ifndef _BTS_SUPPORT_H
+#define _BTS_SUPPORT_H
+
+struct bts_support {
+ /* crypto supprot */
+ uint8_t a5_1;
+ uint8_t a5_2;
+ uint8_t a5_3;
+ uint8_t a5_4;
+ uint8_t a5_5;
+ uint8_t a5_6;
+ uint8_t a5_7;
+ /* radio support */
+ uint8_t freq_map[128];
+ /* codecs */
+ uint8_t chan_comb[256];
+ uint8_t full_v1;
+ uint8_t full_v2;
+ uint8_t full_v3;
+ uint8_t half_v1;
+ uint8_t half_v3;
+};
+
+extern struct bts_support bts_support;
+void bts_support_init(void);
+char *bts_support_comb_name(uint8_t chan_comb);
+
+#endif /* _SUPPORT_H */
+
+
diff --git a/src/common/abis.c b/src/common/abis.c
new file mode 100644
index 00000000..fc90f912
--- /dev/null
+++ b/src/common/abis.c
@@ -0,0 +1,488 @@
+/* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include <osmocore/select.h>
+#include <osmocore/timer.h>
+#include <osmocore/msgb.h>
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/bts/abis.h>
+#include <osmocom/bb/bts/rtp.h>
+#include <osmocom/bb/bts/bts.h>
+#include <osmocom/bb/bts/rsl.h>
+#include <osmocom/bb/bts/oml.h>
+
+extern char *software_version;
+extern uint8_t abis_mac[6];
+
+/*
+ * support
+ */
+
+#define ABIS_ALLOC_SIZE 900
+
+/* send message to BSC */
+int abis_tx(struct ipabis_link *link, struct msgb *msg)
+{
+ if (link->state != LINK_STATE_CONNECT) {
+ LOGP(DABIS, LOGL_NOTICE, "Link down, dropping message.\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+ msgb_enqueue(&link->tx_queue, msg);
+ link->bfd.when |= BSC_FD_WRITE;
+
+ return 0;
+}
+
+
+struct msgb *abis_msgb_alloc(int headroom)
+{
+ struct msgb *nmsg;
+
+ headroom += sizeof(struct ipabis_head);
+
+ nmsg = msgb_alloc_headroom(ABIS_ALLOC_SIZE + headroom,
+ headroom, "Abis/IP");
+ if (!nmsg)
+ return NULL;
+ return nmsg;
+}
+
+void abis_push_ipa(struct msgb *msg, uint8_t proto)
+{
+ struct ipabis_head *nhh;
+
+ msg->l2h = msg->data;
+ nhh = (struct ipabis_head *) msgb_push(msg, sizeof(*nhh));
+ nhh->proto = proto;
+ nhh->len = htons(msgb_l2len(msg));
+}
+
+/*
+ * IPA related messages
+ */
+
+/* send ping/pong */
+static int abis_tx_ipa_pingpong(struct ipabis_link *link, uint8_t pingpong)
+{
+ struct msgb *nmsg;
+
+ nmsg = abis_msgb_alloc(0);
+ if (!nmsg)
+ return -ENOMEM;
+ *msgb_put(nmsg, 1) = pingpong;
+ abis_push_ipa(nmsg, IPA_PROTO_IPACCESS);
+
+ return abis_tx(link, nmsg);
+}
+
+/* send ACK and ID RESP */
+static int abis_rx_ipa_id_get(struct ipabis_link *link, uint8_t *data, int len)
+{
+ struct msgb *nmsg, *nmsg2;
+ char str[64];
+ uint8_t *tag;
+
+ LOGP(DABIS, LOGL_INFO, "Reply to ID_GET\n");
+
+ nmsg = abis_msgb_alloc(0);
+ if (!nmsg)
+ return -ENOMEM;
+ *msgb_put(nmsg, 1) = IPA_MSGT_ID_RESP;
+ while (len) {
+ if (len < 2) {
+ LOGP(DABIS, LOGL_NOTICE,
+ "Short read of ipaccess tag\n");
+ msgb_free(nmsg);
+ return -EIO;
+ }
+ switch (data[1]) {
+ case IPA_IDTAG_UNIT:
+ sprintf(str, "%s/%d", (link->trx) ? link->trx->bts->id
+ : link->bts->id,
+ (link->trx) ? link->trx->trx_nr : 0);
+ break;
+ case IPA_IDTAG_MACADDR:
+ sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
+ abis_mac[0], abis_mac[1], abis_mac[2],
+ abis_mac[3], abis_mac[4], abis_mac[5]);
+ break;
+ case IPA_IDTAG_LOCATION1:
+ strcpy(str, "osmoBTS");
+ break;
+ case IPA_IDTAG_LOCATION2:
+ strcpy(str, "osmoBTS");
+ break;
+ case IPA_IDTAG_EQUIPVERS:
+ case IPA_IDTAG_SWVERSION:
+ strcpy(str, software_version);
+ break;
+ case IPA_IDTAG_UNITNAME:
+ sprintf(str, "osmoBTS-%02x-%02x-%02x-%02x-%02x-%02x",
+ abis_mac[0], abis_mac[1], abis_mac[2],
+ abis_mac[3], abis_mac[4], abis_mac[5]);
+ break;
+ case IPA_IDTAG_SERNR:
+ strcpy(str, "");
+ break;
+ default:
+ LOGP(DABIS, LOGL_NOTICE,
+ "Unknown ipaccess tag 0x%02x\n", *data);
+ msgb_free(nmsg);
+ return -EINVAL;
+ }
+ LOGP(DABIS, LOGL_INFO, " tag %d: %s\n", data[1], str);
+ tag = msgb_put(nmsg, 3 + strlen(str) + 1);
+ tag[0] = 0x00;
+ tag[1] = 1 + strlen(str) + 1;
+ tag[2] = data[1];
+ memcpy(tag + 3, str, strlen(str) + 1);
+ data += 2;
+ len -= 2;
+ }
+ abis_push_ipa(nmsg, IPA_PROTO_IPACCESS);
+
+ nmsg2 = abis_msgb_alloc(0);
+ if (!nmsg2) {
+ msgb_free(nmsg);
+ return -ENOMEM;
+ }
+ *msgb_put(nmsg2, 1) = IPA_MSGT_ID_ACK;
+ abis_push_ipa(nmsg2, IPA_PROTO_IPACCESS);
+
+ link->id_resp = 1;
+
+ abis_tx(link, nmsg2);
+ return abis_tx(link, nmsg);
+}
+
+static int abis_rx_ipaccess(struct ipabis_link *link, struct msgb *msg)
+{
+ uint8_t *data = msgb_l2(msg);
+ int len = msgb_l2len(msg);
+ int ret = 0;
+
+ if (len < 1) {
+ LOGP(DABIS, LOGL_NOTICE, "Short read of ipaccess message\n");
+ msgb_free(msg);
+ return EIO;
+ }
+
+ switch (*data) {
+ case IPA_MSGT_PONG:
+#warning HACK
+rsl_tx_chan_rqd(link->bts->trx[0]);
+ LOGP(DABIS, LOGL_INFO, "PONG\n");
+ link->pong = 1;
+ break;
+ case IPA_MSGT_PING:
+ LOGP(DABIS, LOGL_INFO, "reply to ping request\n");
+ ret = abis_tx_ipa_pingpong(link, IPA_MSGT_PONG);
+ break;
+ case IPA_MSGT_ID_GET:
+ ret = abis_rx_ipa_id_get(link, data + 1, len - 1);
+ break;
+ case IPA_MSGT_ID_ACK:
+ LOGP(DABIS, LOGL_INFO, "ID ACK\n");
+ if (link->id_resp && link->bts)
+ ret = bts_link_estab(link->bts);
+ if (link->id_resp && link->trx)
+ ret = trx_link_estab(link->trx);
+ link->id_resp = 0;
+
+ break;
+ default:
+ LOGP(DABIS, LOGL_NOTICE,
+ "Unknown ipaccess message type 0x%02x\n", *data);
+ ret = -EINVAL;
+ }
+
+ msgb_free(msg);
+
+ return ret;
+}
+
+/*
+ * A-Bis over IP implementation
+ */
+
+/* receive message from BSC */
+static int abis_rx(struct ipabis_link *link, struct msgb *msg)
+{
+ struct ipabis_head *hh = (struct ipabis_head *) msg->data;
+ int ret = 0;
+
+ switch (hh->proto) {
+ case IPA_PROTO_RSL:
+ if (!link->trx) {
+ LOGP(DABIS, LOGL_NOTICE,
+ "Received RSL message not on RSL link\n");
+ msgb_free(msg);
+ ret = EIO;
+ break;
+ }
+ ret = down_rsl(link->trx, msg);
+ break;
+ case IPA_PROTO_IPACCESS:
+ ret = abis_rx_ipaccess(link, msg);
+ break;
+ case IPA_PROTO_SCCP:
+ LOGP(DABIS, LOGL_INFO, "Received SCCP message\n");
+ msgb_free(msg);
+ break;
+ case IPA_PROTO_OML:
+ if (!link->bts) {
+ LOGP(DABIS, LOGL_NOTICE,
+ "Received OML message not on OML link\n");
+ msgb_free(msg);
+ ret = EIO;
+ break;
+ }
+ ret = down_oml(link->bts, msg);
+ break;
+ default:
+ LOGP(DABIS, LOGL_NOTICE, "Unknown Protocol %d\n", hh->proto);
+ msgb_free(msg);
+ ret = EINVAL;
+ }
+
+ return ret;
+}
+
+static void abis_timeout(void *arg)
+{
+ struct ipabis_link *link = arg;
+ int ret;
+
+ switch (link->state) {
+ case LINK_STATE_RETRYING:
+ ret = abis_open(link, link->ip);
+ if (ret <= 0)
+ bsc_schedule_timer(&link->timer, OML_RETRY_TIMER, 0);
+ break;
+ case LINK_STATE_CONNECT:
+ if (link->ping && !link->pong) {
+ LOGP(DABIS, LOGL_NOTICE,
+ "No reply to PING. Link is lost\n");
+ abis_close(link);
+ ret = abis_open(link, link->ip);
+ if (ret <= 0) {
+ bsc_schedule_timer(&link->timer,
+ OML_RETRY_TIMER, 0);
+ link->state = LINK_STATE_RETRYING;
+ }
+ break;
+ }
+ link->ping = 1;
+ link->pong = 0;
+ LOGP(DABIS, LOGL_INFO, "PING\n");
+ abis_tx_ipa_pingpong(link, IPA_MSGT_PING);
+ bsc_schedule_timer(&link->timer, OML_PING_TIMER, 0);
+ break;
+ }
+}
+
+static int abis_sock_cb(struct bsc_fd *bfd, unsigned int what)
+{
+ struct ipabis_link *link = bfd->data;
+ struct ipabis_head *hh;
+ struct msgb *msg;
+ int ret = 0;
+
+ if ((what & BSC_FD_WRITE) && link->state == LINK_STATE_CONNECTING) {
+ if (link->bts) {
+ if (bsc_timer_pending(&link->timer))
+ bsc_del_timer(&link->timer);
+// bsc_schedule_timer(&link->timer, OML_PING_TIMER, 0);
+#warning HACK
+ bsc_schedule_timer(&link->timer, 3, 0);
+ link->ping = link->pong = 0;
+ }
+ LOGP(DABIS, LOGL_INFO, "Abis socket now connected.\n");
+ link->state = LINK_STATE_CONNECT;
+ }
+//printf("what %d\n", what);
+
+ if ((what & BSC_FD_READ)) {
+ if (!link->rx_msg) {
+ link->rx_msg = msgb_alloc(ABIS_ALLOC_SIZE, "Abis/IP");
+ if (!link->rx_msg)
+ return -ENOMEM;
+ }
+ msg = link->rx_msg;
+ hh = (struct ipabis_head *) msg->data;
+ if (msg->len < sizeof(*hh)) {
+ ret = recv(link->bfd.fd, msg->data, sizeof(*hh), 0);
+ if (ret <= 0) {
+ goto close;
+ }
+ msgb_put(msg, ret);
+ if (msg->len < sizeof(*hh))
+ return 0;
+ msg->l2h = msg->data + sizeof(*hh);
+ if (ntohs(hh->len) > msgb_tailroom(msg)) {
+ LOGP(DABIS, LOGL_NOTICE, "Received packet from "
+ "Abis socket too large.\n");
+ goto close;
+ }
+ }
+ ret = recv(link->bfd.fd, msg->tail,
+ ntohs(hh->len) + sizeof(*hh) - msg->len, 0);
+ if (ret == 0)
+ goto close;
+ if (ret < 0 && errno != EAGAIN)
+ goto close;
+ msgb_put(msg, ret);
+ if (ntohs(hh->len) + sizeof(*hh) > msg->len)
+ return 0;
+ link->rx_msg = NULL;
+ LOGP(DABIS, LOGL_INFO, "Received messages from Abis socket.\n");
+ ret = abis_rx(link, msg);
+ }
+ if ((what & BSC_FD_WRITE)) {
+ msg = msgb_dequeue(&link->tx_queue);
+ if (msg) {
+ LOGP(DABIS, LOGL_INFO, "Sending messages to Abis socket.\n");
+ ret = send(link->bfd.fd, msg->data, msg->len, 0);
+ if (ret < 0)
+ goto close;
+ } else
+ link->bfd.when &= ~BSC_FD_WRITE;
+ }
+ if ((what & BSC_FD_EXCEPT)) {
+ LOGP(DABIS, LOGL_INFO, "Abis socket received exception\n");
+ goto close;
+ }
+
+ return ret;
+
+close:
+ abis_close(link);
+
+ /* RSL link will just close and BSC is notified */
+ if (link->trx) {
+ LOGP(DABIS, LOGL_INFO, "Connection to BSC failed\n");
+ return trx_link_estab(link->trx);
+ }
+
+ LOGP(DABIS, LOGL_INFO, "Connection to BSC failed, retrying in %d "
+ "seconds.\n", OML_RETRY_TIMER);
+ bsc_schedule_timer(&link->timer, OML_RETRY_TIMER, 0);
+ link->state = LINK_STATE_RETRYING;
+
+ return 0;
+}
+
+int abis_open(struct ipabis_link *link, uint32_t ip)
+{
+ unsigned int on = 1;
+ struct sockaddr_in addr;
+ int sock;
+ int ret;
+
+ if (link->bfd.fd > 0)
+ return -EBUSY;
+
+ INIT_LLIST_HEAD(&link->tx_queue);
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
+ return sock;
+
+ ret = ioctl(sock, FIONBIO, (unsigned char *)&on);
+ if (ret < 0) {
+ close(sock);
+ return ret;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ if (link->bts)
+ addr.sin_port = htons(IPA_TCP_PORT_OML);
+ else
+ addr.sin_port = htons(IPA_TCP_PORT_RSL);
+ addr.sin_addr.s_addr = htonl(ip);
+
+ ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
+ if (ret < 0 && errno != EINPROGRESS) {
+ close(sock);
+ return ret;
+ }
+
+ link->bfd.data = link;
+ link->bfd.when = BSC_FD_READ | BSC_FD_WRITE | BSC_FD_EXCEPT;
+ link->bfd.cb = abis_sock_cb;
+ link->bfd.fd = sock;
+ link->state = LINK_STATE_CONNECTING;
+ link->ip = ip;
+ link->timer.cb = abis_timeout;
+ link->timer.data = link;
+
+ bsc_register_fd(&link->bfd);
+
+ LOGP(DABIS, LOGL_INFO, "Abis socket trying to reach BSC.\n");
+
+ return sock;
+}
+
+void abis_close(struct ipabis_link *link)
+{
+ struct msgb *msg;
+ int i;
+
+ if (link->bfd.fd <= 0)
+ return;
+
+ LOGP(DABIS, LOGL_INFO, "Abis socket closed.\n");
+
+#warning if any link fails, shutdown all links
+ if (link->trx) {
+ for (i = 0; i < 8; i++)
+ bts_setup_slot(&link->trx->slot[i], 0xff);
+ }
+
+ if (link->rx_msg) {
+ msgb_free(link->rx_msg);
+ link->rx_msg = NULL;
+ }
+
+ while ((msg = msgb_dequeue(&link->tx_queue)))
+ msgb_free(msg);
+
+ bsc_unregister_fd(&link->bfd);
+
+ close(link->bfd.fd);
+ link->bfd.fd = -1; /* -1 or 0 indicates: 'close' */
+ link->state = LINK_STATE_IDLE;
+
+ if (bsc_timer_pending(&link->timer))
+ bsc_del_timer(&link->timer);
+}
+
diff --git a/src/common/bts.c b/src/common/bts.c
new file mode 100644
index 00000000..668fd917
--- /dev/null
+++ b/src/common/bts.c
@@ -0,0 +1,376 @@
+/* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <osmocore/select.h>
+#include <osmocore/timer.h>
+#include <osmocore/msgb.h>
+#include <osmocore/talloc.h>
+#include <osmocore/protocol/gsm_12_21.h>
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/l1l2_interface.h>
+#include <osmocom/bb/bts/abis.h>
+#include <osmocom/bb/bts/rtp.h>
+#include <osmocom/bb/bts/bts.h>
+#include <osmocom/bb/bts/rsl.h>
+#include <osmocom/bb/bts/oml.h>
+
+extern void *l23_ctx;
+
+extern uint8_t cr_loc2rem_cmd;
+extern uint8_t cr_loc2rem_resp;
+extern uint8_t cr_rem2loc_cmd;
+extern uint8_t cr_rem2loc_resp;
+
+BTS_SI_NAME;
+
+#if 0
+struct osmobts_lchan *lchan_by_channelnr(struct osmobts_trx *trx,
+ uint8_t channelnr)
+{
+ uint8_t ts = channelnr & ~RSL_CHAN_NR_MASK;
+ uint8_t type = channelnr & RSL_CHAN_NR_MASK;
+ uint8_t sub = 0;
+ struct osmobts_slot *slot = trx->slot[ts];
+
+ if ((type & 0xf0) == RSL_CHAN_Lm_ACCHs) {
+ sub = (channelnr >> 3) & 1;
+ type = RSL_CHAN_Lm_ACCHs;
+ } else
+ if ((type & 0xe0) == RSL_CHAN_SDCCH4_ACCH) {
+ sub = (channelnr >> 3) & 3;
+ type = RSL_CHAN_SDCCH4_ACCH;
+ } else
+ if ((type & 0xc0) == RSL_CHAN_SDCCH8_ACCH) {
+ sub = (channelnr >> 3) & 7;
+ type = RSL_CHAN_SDCCH8_ACCH;
+ } else
+ if (type == RSL_CHAN_BCCH
+ || type == RSL_CHAN_RACH
+ || type == RSL_CHAN_PCH_AGCH) {
+ if (!slot->has_bcch) {
+ LOGP(DSUM, LOGL_NOTICE, "Slot %d has no BCCH\n", ts);
+ return NULL;
+ }
+ return slot->lchan[0];
+ } else {
+ LOGP(DSUM, LOGL_NOTICE, "Unknown channel type 0x%02x\n", type);
+ return NULL;
+ }
+
+ if (slot->acch_type == type)
+ return slot->lchan[sub];
+ }
+ LOGP(DSUM, LOGL_NOTICE, "No slot found with channel type 0x%02x\n", type);
+ return NULL;
+
+}
+#endif
+
+struct osmocom_bts *create_bts(uint8_t num_trx, char *id)
+{
+ struct osmocom_bts *bts;
+ struct osmobts_trx *trx;
+ int i, j;
+
+ LOGP(DSUM, LOGL_INFO, "Creating BTS\n");
+ bts = talloc_zero(l23_ctx, struct osmocom_bts);
+ if (!bts)
+ return NULL;
+ bts->link.bts = bts;
+ bts->id = id;
+ for (i = 0; i < num_trx; i++) {
+ LOGP(DSUM, LOGL_INFO, "Creating TRX %d\n", i);
+ trx = talloc_zero(l23_ctx, struct osmobts_trx);
+ if (!trx)
+ return NULL;
+ trx->bts = bts;
+ trx->trx_nr = i;
+ INIT_LLIST_HEAD(&trx->ms_list);
+ for (j = 0; j < 8; j++) {
+ trx->slot[j].trx = trx;
+ trx->slot[j].slot_nr = j;
+ trx->slot[j].chan_comb = 0xff; /* not set */
+ }
+ trx->link.trx = trx;
+ bts->trx[i] = trx;
+ }
+ bts->num_trx = num_trx;
+
+ return bts;
+}
+
+int create_ms(struct osmobts_trx *trx, int maskc, uint8_t *maskv_tx,
+ uint8_t *maskv_rx)
+{
+ struct osmobts_ms *tx_ms, *rx_ms;
+ int i, j;
+ int rc;
+ static char sock_path[] = "/tmp/osmocom_l2.1";
+
+ for (i = 0; i < maskc; i++) {
+ /* create MS */
+ tx_ms = talloc_zero(l23_ctx, struct osmobts_ms);
+ printf("%p\n", tx_ms);
+ if (!tx_ms)
+ return -ENOMEM;
+ tx_ms->trx = trx;
+ rc = layer2_open(&tx_ms->ms, sock_path);
+ strcpy(tx_ms->ms.name, strchr(sock_path, '.') + 1);
+ sock_path[strlen(sock_path) - 1]++;
+ if (rc < 0) {
+ talloc_free(tx_ms);
+ return rc;
+ }
+ llist_add_tail(&tx_ms->entry, &trx->ms_list);
+ rx_ms = talloc_zero(l23_ctx, struct osmobts_ms);
+ rx_ms->trx = trx;
+ if (!rx_ms)
+ return -ENOMEM;
+ rc = layer2_open(&rx_ms->ms, sock_path);
+ strcpy(rx_ms->ms.name, strchr(sock_path, '.') + 1);
+ sock_path[strlen(sock_path) - 1]++;
+ if (rc < 0) {
+ talloc_free(rx_ms);
+ return rc;
+ }
+ llist_add_tail(&rx_ms->entry, &trx->ms_list);
+ /* assign to SLOT */
+ for (j = 0; j < 8; j++) {
+ if ((maskv_tx[i] & (1 << j)))
+ trx->slot[j].tx_ms = tx_ms;
+ if ((maskv_rx[i] & (1 << j)))
+ trx->slot[j].rx_ms = rx_ms;
+ }
+ }
+
+ return i;
+}
+
+void destroy_lchan(struct osmobts_lchan *lchan)
+{
+ LOGP(DSUM, LOGL_INFO, "Destroying logical channel. (trx=%d ts=%d ss=%d)\n", lchan->slot->trx->trx_nr, lchan->slot->slot_nr, lchan->lchan_nr);
+ lapdm_exit(&lchan->l2_entity.lapdm_acch);
+ lapdm_exit(&lchan->l2_entity.lapdm_acch);
+ if (lchan->rtp.socket_created)
+ rtp_close_socket(&lchan->rtp);
+ talloc_free(lchan);
+}
+
+/* create and destroy lchan */
+void bts_setup_slot(struct osmobts_slot *slot, uint8_t comb)
+{
+ int i, ii;
+ struct osmobts_lchan *lchan;
+ uint8_t cbits;
+
+ if (slot->chan_comb == comb)
+ return;
+
+ /* destroy old */
+ for (i = 0; i < 8; i++) {
+ if (slot->lchan[i])
+ destroy_lchan(slot->lchan[i]);
+ slot->lchan[i] = NULL;
+ }
+ switch(comb) {
+ case 0xff:
+ return;
+ case NM_CHANC_TCHFull:
+ cbits = 0x01;
+ ii = 1;
+ break;
+ case NM_CHANC_TCHHalf:
+ cbits = 0x02;
+ ii = 2;
+ break;
+ case NM_CHANC_BCCHComb:
+ cbits = 0x04;
+ ii = 4;
+ break;
+ case NM_CHANC_SDCCH:
+ cbits = 0x08;
+ ii = 8;
+ break;
+ default:
+ cbits = 0x10;
+ ii = 0;
+ }
+
+ if (!slot->tx_ms) {
+ LOGP(DSUM, LOGL_ERROR, "Slot is not available\n");
+ return;
+ }
+
+ for (i = 0; i < ii; i++) {
+ LOGP(DSUM, LOGL_INFO, "Creating logical channel. (trx=%d ts=%d ss=%d)\n", slot->trx->trx_nr, slot->slot_nr, i);
+ lchan = talloc_zero(l23_ctx, struct osmobts_lchan);
+ lchan->slot = slot;
+ lchan->lchan_nr = i;
+ lchan->chan_nr = ((cbits | i) << 3) | slot->slot_nr;
+ lapdm_init(&lchan->l2_entity.lapdm_dcch, &lchan->l2_entity, &slot->tx_ms->ms);
+ lapdm_init(&lchan->l2_entity.lapdm_acch, &lchan->l2_entity, &slot->tx_ms->ms);
+ lapdm_set_bts();
+ osmol2_register_handler(&lchan->l2_entity, &rsl_tx_rll);
+ slot->lchan[i] = lchan;
+ }
+
+}
+
+static void destroy_ms(struct osmobts_ms *ms)
+{
+ layer2_close(&ms->ms);
+ llist_del(&ms->entry);
+ talloc_free(ms);
+}
+
+void destroy_bts(struct osmocom_bts *bts)
+{
+ int i;
+ struct osmobts_ms *ms;
+
+ for (i = 0; i < bts->num_trx; i++) {
+ abis_close(&bts->trx[i]->link);
+ while(!llist_empty(&bts->trx[i]->ms_list)) {
+ ms = llist_entry(bts->trx[i]->ms_list.next,
+ struct osmobts_ms, entry);
+ destroy_ms(ms);
+ }
+ if (bsc_timer_pending(&bts->trx[i]->si.timer))
+ bsc_del_timer(&bts->trx[i]->si.timer);
+ talloc_free(bts->trx[i]);
+ bts->trx[i] = NULL;
+ }
+ abis_close(&bts->link);
+ talloc_free(bts);
+}
+
+/* main link is established, send status report */
+int bts_link_estab(struct osmocom_bts *bts)
+{
+ int i, j;
+ uint8_t radio_state;
+
+ LOGP(DSUM, LOGL_INFO, "Main link established, sending Status'.\n");
+
+ /* site-manager status */
+ oml_tx_state_changed(&bts->link, NM_OPSTATE_ENABLED, NM_AVSTATE_OK,
+ NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff);
+ /* bts status */
+ oml_tx_state_changed(&bts->link, NM_OPSTATE_ENABLED, NM_AVSTATE_OK,
+ NM_OC_BTS, 0, 0xff, 0xff);
+ /* trx */
+ for (i = 0; i < bts->num_trx; i++) {
+ radio_state = (bts->trx[i]->link.state == LINK_STATE_CONNECT) ? NM_OPSTATE_ENABLED : NM_OPSTATE_DISABLED;
+ oml_tx_state_changed(&bts->link, radio_state,
+ NM_AVSTATE_OK, NM_OC_RADIO_CARRIER, 0,
+ bts->trx[i]->trx_nr, 0xff);
+ oml_tx_sw_act_rep(&bts->link, NM_OC_RADIO_CARRIER, 0, bts->trx[i]->trx_nr, 0xff);
+ oml_tx_state_changed(&bts->link, NM_OPSTATE_ENABLED,
+ NM_AVSTATE_OK, NM_OC_BASEB_TRANSC, 0,
+ bts->trx[i]->trx_nr, 0xff);
+ oml_tx_sw_act_rep(&bts->link, NM_OC_BASEB_TRANSC, 0, bts->trx[i]->trx_nr, 0xff);
+ /* channel */
+ for (j = 0; j < 8; j++) {
+ if (bts->trx[i]->slot[j].tx_ms)
+ oml_tx_state_changed(&bts->link,
+ NM_OPSTATE_DISABLED, NM_AVSTATE_DEPENDENCY,
+ NM_OC_CHANNEL, 0, bts->trx[i]->trx_nr,
+ bts->trx[i]->slot[j].slot_nr);
+ else
+ oml_tx_state_changed(&bts->link,
+ NM_OPSTATE_DISABLED,
+ NM_AVSTATE_NOT_INSTALLED,
+ NM_OC_CHANNEL, 0, bts->trx[i]->trx_nr,
+ bts->trx[i]->slot[j].slot_nr);
+ }
+ }
+
+ return 0;
+}
+
+/* RSL link is established, send status report */
+int trx_link_estab(struct osmobts_trx *trx)
+{
+ uint8_t radio_state = (trx->link.state == LINK_STATE_CONNECT) ? NM_OPSTATE_ENABLED : NM_OPSTATE_DISABLED;
+
+ LOGP(DSUM, LOGL_INFO, "RSL link (TRX %02x) state changed to %s, sending Status'.\n", trx->trx_nr, (trx->link.state == LINK_STATE_CONNECT) ? "up" : "down");
+
+ oml_tx_state_changed(&trx->bts->link, radio_state, NM_AVSTATE_OK, NM_OC_RADIO_CARRIER, 0, trx->trx_nr, 0xff);
+
+ if (trx->link.state == LINK_STATE_CONNECT)
+ rsl_tx_rf_res(trx);
+
+ return 0;
+}
+
+void bts_new_si(void *arg)
+{
+ struct osmobts_trx *trx = arg;
+ int i;
+
+ if (bsc_timer_pending(&trx->si.timer))
+ return;
+
+ i = 0;
+ while(i < BTS_SI_NUM) {
+ if ((trx->si.flags[i] & BTS_SI_NEW))
+ break;
+ i++;
+ }
+ if (i == BTS_SI_NUM)
+ return;
+ if ((trx->si.flags[i] & BTS_SI_USE))
+ LOGP(DSUM, LOGL_INFO, "Setting SYSTEM INFORMATION %s.\n", bts_si_name[i]);
+ else
+ LOGP(DSUM, LOGL_INFO, "Removing SYSTEM INFORMATION %s.\n", bts_si_name[i]);
+ trx->si.flags[i] &= ~BTS_SI_NEW;
+ /* distribute */
+ printf("TODO: send SI update to L1\n");
+ /* delay until next SI */
+ trx->si.timer.cb = bts_new_si;
+ trx->si.timer.data = trx;
+ bsc_schedule_timer(&trx->si.timer, 0, 200000);
+}
+
+/* handle bts instance (including MS instances) */
+int work_bts(struct osmocom_bts *bts)
+{
+ int work = 0, w;
+
+ do {
+ w = 0;
+// w |= xxx_dequeue(ms);
+ if (w)
+ work = 1;
+ } while (w);
+ return work;
+}
+#if 0
+int create_chan(struct osmocom_bts *slot, uint8_t type)
+{
+ welcher type?:
+}
+#endif
+
diff --git a/src/common/l1ctl.c b/src/common/l1ctl.c
new file mode 100644
index 00000000..b99af2ff
--- /dev/null
+++ b/src/common/l1ctl.c
@@ -0,0 +1,305 @@
+/* Layer1 control code, talking L1CTL protocol with L1 on the phone */
+
+/* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <l1ctl_proto.h>
+
+#include <osmocore/signal.h>
+#include <osmocore/logging.h>
+#include <osmocore/timer.h>
+#include <osmocore/msgb.h>
+#include <osmocore/tlv.h>
+#include <osmocore/gsm_utils.h>
+#include <osmocore/gsmtap_util.h>
+#include <osmocore/protocol/gsm_04_08.h>
+#include <osmocore/protocol/gsm_08_58.h>
+#include <osmocore/rsl.h>
+
+#include <osmocom/bb/common/l1ctl.h>
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/l1l2_interface.h>
+#include <osmocom/bb/common/lapdm.h>
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/bts/abis.h>
+#include <osmocom/bb/bts/rtp.h>
+#include <osmocom/bb/bts/bts.h>
+
+static struct msgb *osmo_l1_alloc(uint8_t msg_type)
+{
+ struct l1ctl_hdr *l1h;
+ struct msgb *msg = msgb_alloc_headroom(256, 4, "osmo_l1");
+
+ if (!msg) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory.\n");
+ return NULL;
+ }
+
+ msg->l1h = msgb_put(msg, sizeof(*l1h));
+ l1h = (struct l1ctl_hdr *) msg->l1h;
+ l1h->msg_type = msg_type;
+
+ return msg;
+}
+
+
+static int osmo_make_band_arfcn(struct osmocom_ms *ms, uint16_t arfcn)
+{
+ /* TODO: Include the band */
+ return arfcn;
+}
+
+/* Receive L1CTL_DATA_IND (Data Indication from L1) */
+int rx_ph_data_ind(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct l1ctl_info_dl *dl, dl_cpy;
+ struct l1ctl_data_ind *ccch;
+ struct lapdm_entity *le;
+ struct rx_meas_stat *meas = &ms->meas;
+ uint8_t chan_type, chan_ts, chan_ss;
+ uint8_t gsmtap_chan_type;
+ struct gsm_time tm;
+ struct osmobts_ms *bts_ms = container_of(ms, struct osmobts_ms, ms);
+ struct osmobts_lchan *lchan;
+
+ if (msgb_l3len(msg) < sizeof(*ccch)) {
+ LOGP(DL1C, LOGL_ERROR, "MSG too short Data Ind: %u\n",
+ msgb_l3len(msg));
+ msgb_free(msg);
+ return -1;
+ }
+
+ dl = (struct l1ctl_info_dl *) msg->l1h;
+ msg->l2h = dl->payload;
+ ccch = (struct l1ctl_data_ind *) msg->l2h;
+
+ gsm_fn2gsmtime(&tm, ntohl(dl->frame_nr));
+ rsl_dec_chan_nr(dl->chan_nr, &chan_type, &chan_ss, &chan_ts);
+ lchan = bts_ms->trx->slot[chan_ts].lchan[chan_ss];
+ if (!lchan) {
+ LOGP(DL1C, LOGL_ERROR, "Data IND for non existing lchan\n");
+ msgb_free(msg);
+ return -1;
+ }
+ LOGP(DL1C, LOGL_NOTICE, "RX: %s | %s(%.4u/%.2u/%.2u) %d dBm\n",
+ rsl_chan_nr_str(dl->chan_nr),
+ hexdump(ccch->data, sizeof(ccch->data)),
+ tm.t1, tm.t2, tm.t3, (int)dl->rx_level-110);
+
+ meas->last_fn = ntohl(dl->frame_nr);
+ meas->frames++;
+ meas->snr += dl->snr;
+ meas->berr += dl->num_biterr;
+ meas->rxlev += dl->rx_level;
+
+ if (dl->fire_crc >= 2) {
+ LOGP(DL1C, LOGL_NOTICE, "Dropping frame with %u bit errors\n",
+ dl->num_biterr);
+ msgb_free(msg);
+ return 0;
+ }
+
+ /* send CCCH data via GSMTAP */
+ gsmtap_chan_type = chantype_rsl2gsmtap(chan_type, dl->link_id);
+ gsmtap_sendmsg(ntohs(dl->band_arfcn), chan_ts, gsmtap_chan_type, chan_ss,
+ tm.fn, dl->rx_level-110, dl->snr, ccch->data,
+ sizeof(ccch->data));
+
+ /* determine LAPDm entity based on SACCH or not */
+ if (dl->link_id & 0x40)
+ le = &lchan->l2_entity.lapdm_acch;
+ else
+ le = &lchan->l2_entity.lapdm_dcch;
+ /* make local stack copy of l1ctl_info_dl, as LAPDm will
+ * overwrite skb hdr */
+ memcpy(&dl_cpy, dl, sizeof(dl_cpy));
+
+ /* pull the L1 header from the msgb */
+ msgb_pull(msg, msg->l2h - (msg->l1h-sizeof(struct l1ctl_hdr)));
+ msg->l1h = NULL;
+
+ /* send it up into LAPDm */
+ l2_ph_data_ind(msg, le, &dl_cpy);
+
+ return 0;
+}
+
+/* Receive L1CTL_DATA_CONF (Data Confirm from L1) */
+static int rx_ph_data_conf(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct l1ctl_info_dl *dl;
+ struct lapdm_entity *le;
+ uint8_t chan_type, chan_ts, chan_ss;
+ struct osmobts_ms *bts_ms = container_of(ms, struct osmobts_ms, ms);
+ struct osmobts_lchan *lchan;
+
+ dl = (struct l1ctl_info_dl *) msg->l1h;
+
+ rsl_dec_chan_nr(dl->chan_nr, &chan_type, &chan_ss, &chan_ts);
+ lchan = bts_ms->trx->slot[chan_ts].lchan[chan_ss];
+ if (!lchan) {
+ LOGP(DL1C, LOGL_ERROR, "Data IND for non existing lchan\n");
+ msgb_free(msg);
+ return -1;
+ }
+
+ /* determine LAPDm entity based on SACCH or not */
+ if (dl->link_id & 0x40)
+ le = &lchan->l2_entity.lapdm_acch;
+ else
+ le = &lchan->l2_entity.lapdm_dcch;
+
+ /* send it up into LAPDm */
+ l2_ph_data_conf(msg, le);
+
+ return 0;
+}
+
+/* Transmit L1CTL_DATA_REQ */
+int l1ctl_tx_data_req(struct osmocom_ms *ms, struct msgb *msg,
+ uint8_t chan_nr, uint8_t link_id)
+{
+ struct l1ctl_hdr *l1h;
+ struct l1ctl_info_ul *l1i_ul;
+ uint8_t chan_type, chan_ts, chan_ss;
+ uint8_t gsmtap_chan_type;
+
+ if (msgb_l2len(msg) > 23) {
+ LOGP(DL1C, LOGL_ERROR, "L1 cannot handle message length "
+ "> 23 (%u)\n", msgb_l2len(msg));
+ msgb_free(msg);
+ return -EINVAL;
+ } else if (msgb_l2len(msg) < 23)
+ LOGP(DL1C, LOGL_ERROR, "L1 message length < 23 (%u) "
+ "doesn't seem right!\n", msgb_l2len(msg));
+
+ /* send copy via GSMTAP */
+ rsl_dec_chan_nr(chan_nr, &chan_type, &chan_ss, &chan_ts);
+ gsmtap_chan_type = chantype_rsl2gsmtap(chan_type, link_id);
+ gsmtap_sendmsg(0|0x4000, chan_ts, gsmtap_chan_type, chan_ss,
+ 0, 127, 255, msg->l2h, msgb_l2len(msg));
+
+ LOGP(DL1C, LOGL_NOTICE, "TX: %s | %s\n", rsl_chan_nr_str(chan_nr),
+ hexdump(msg->l2h, msgb_l2len(msg)));
+
+ /* prepend uplink info header */
+ l1i_ul = (struct l1ctl_info_ul *) msgb_push(msg, sizeof(*l1i_ul));
+
+ l1i_ul->chan_nr = chan_nr;
+ l1i_ul->link_id = link_id;
+
+ /* prepend l1 header */
+ msg->l1h = msgb_push(msg, sizeof(*l1h));
+ l1h = (struct l1ctl_hdr *) msg->l1h;
+ l1h->msg_type = L1CTL_DATA_REQ;
+
+ printf("todo: transcode message\n");
+
+ return osmo_send_l1(ms, msg);
+}
+
+/* Transmit L1CTL_RESET_REQ */
+int l1ctl_tx_reset_req(struct osmocom_ms *ms, uint8_t type)
+{
+ struct msgb *msg;
+ struct l1ctl_reset *res;
+
+ msg = osmo_l1_alloc(L1CTL_RESET_REQ);
+ if (!msg)
+ return -1;
+
+ LOGP(DL1C, LOGL_INFO, "Tx Reset Req (%u)\n", type);
+ res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res));
+ res->type = type;
+
+ return osmo_send_l1(ms, msg);
+}
+
+/* Receive L1CTL_RESET_IND */
+static int rx_l1_reset(struct osmocom_ms *ms)
+{
+ LOGP(DL1C, LOGL_INFO, "Layer1 Reset indication\n");
+ dispatch_signal(SS_L1CTL, S_L1CTL_RESET, ms);
+
+ return 0;
+}
+
+/* Receive incoming data from L1 using L1CTL format */
+int l1ctl_recv(struct osmocom_ms *ms, struct msgb *msg)
+{
+ int rc = 0;
+ struct l1ctl_hdr *l1h;
+ struct l1ctl_info_dl *dl;
+
+ if (msgb_l2len(msg) < sizeof(*dl)) {
+ LOGP(DL1C, LOGL_ERROR, "Short Layer2 message: %u\n",
+ msgb_l2len(msg));
+ msgb_free(msg);
+ return -1;
+ }
+
+ l1h = (struct l1ctl_hdr *) msg->l1h;
+
+ /* move the l1 header pointer to point _BEHIND_ l1ctl_hdr,
+ as the l1ctl header is of no interest to subsequent code */
+ msg->l1h = l1h->data;
+
+ switch (l1h->msg_type) {
+ case L1CTL_DATA_IND:
+ rc = rx_ph_data_ind(ms, msg);
+ break;
+ case L1CTL_DATA_CONF:
+ rc = rx_ph_data_conf(ms, msg);
+ break;
+ case L1CTL_RESET_IND:
+ case L1CTL_RESET_CONF:
+ rc = rx_l1_reset(ms);
+ msgb_free(msg);
+ break;
+ default:
+ LOGP(DL1C, LOGL_ERROR, "Unknown MSG: %u\n", l1h->msg_type);
+ msgb_free(msg);
+ break;
+ }
+
+ return rc;
+}
+
+int l1ctl_tx_param_req(struct osmocom_ms *ms, uint8_t ta, uint8_t tx_power)
+{
+ return -ENOTSUP;
+}
+
+int l1ctl_tx_rach_req(struct osmocom_ms *ms, uint8_t ra, uint16_t offset,
+ uint8_t combined)
+{
+ return -ENOTSUP;
+}
+
+
diff --git a/src/common/oml.c b/src/common/oml.c
new file mode 100644
index 00000000..62be30b5
--- /dev/null
+++ b/src/common/oml.c
@@ -0,0 +1,549 @@
+/*
+ * (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/*
+ * Operation and Maintainance Messages
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+
+#include <osmocore/protocol/gsm_12_21.h>
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/bts/support.h>
+#include <osmocom/bb/bts/abis.h>
+#include <osmocom/bb/bts/rtp.h>
+#include <osmocom/bb/bts/bts.h>
+#include <osmocom/bb/bts/rsl.h>
+#include <osmocom/bb/bts/oml.h>
+
+/*
+ * support
+ */
+
+/* FIXME: move this to osmocore */
+const struct tlv_definition nm_att_tlvdef = {
+ .def = {
+ [NM_ATT_ABIS_CHANNEL] = { TLV_TYPE_FIXED, 3 },
+ [NM_ATT_ADD_INFO] = { TLV_TYPE_TL16V },
+ [NM_ATT_ADD_TEXT] = { TLV_TYPE_TL16V },
+ [NM_ATT_ADM_STATE] = { TLV_TYPE_TV },
+ [NM_ATT_ARFCN_LIST]= { TLV_TYPE_TL16V },
+ [NM_ATT_AUTON_REPORT] = { TLV_TYPE_TV },
+ [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TL16V },
+ [NM_ATT_BCCH_ARFCN] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_BSIC] = { TLV_TYPE_TV },
+ [NM_ATT_BTS_AIR_TIMER] = { TLV_TYPE_TV },
+ [NM_ATT_CCCH_L_I_P] = { TLV_TYPE_TV },
+ [NM_ATT_CCCH_L_T] = { TLV_TYPE_TV },
+ [NM_ATT_CHAN_COMB] = { TLV_TYPE_TV },
+ [NM_ATT_CONN_FAIL_CRIT] = { TLV_TYPE_TL16V },
+ [NM_ATT_DEST] = { TLV_TYPE_TL16V },
+ [NM_ATT_EVENT_TYPE] = { TLV_TYPE_TV },
+ [NM_ATT_FILE_DATA] = { TLV_TYPE_TL16V },
+ [NM_ATT_FILE_ID] = { TLV_TYPE_TL16V },
+ [NM_ATT_FILE_VERSION] = { TLV_TYPE_TL16V },
+ [NM_ATT_GSM_TIME] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_HSN] = { TLV_TYPE_TV },
+ [NM_ATT_HW_CONFIG] = { TLV_TYPE_TL16V },
+ [NM_ATT_HW_DESC] = { TLV_TYPE_TL16V },
+ [NM_ATT_INTAVE_PARAM] = { TLV_TYPE_TV },
+ [NM_ATT_INTERF_BOUND] = { TLV_TYPE_FIXED, 6 },
+ [NM_ATT_LIST_REQ_ATTR] = { TLV_TYPE_TL16V },
+ [NM_ATT_MAIO] = { TLV_TYPE_TV },
+ [NM_ATT_MANUF_STATE] = { TLV_TYPE_TV },
+ [NM_ATT_MANUF_THRESH] = { TLV_TYPE_TL16V },
+ [NM_ATT_MANUF_ID] = { TLV_TYPE_TL16V },
+ [NM_ATT_MAX_TA] = { TLV_TYPE_TV },
+ [NM_ATT_MDROP_LINK] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_MDROP_NEXT] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_NACK_CAUSES] = { TLV_TYPE_TV },
+ [NM_ATT_NY1] = { TLV_TYPE_TV },
+ [NM_ATT_OPER_STATE] = { TLV_TYPE_TV },
+ [NM_ATT_OVERL_PERIOD] = { TLV_TYPE_TL16V },
+ [NM_ATT_PHYS_CONF] = { TLV_TYPE_TL16V },
+ [NM_ATT_POWER_CLASS] = { TLV_TYPE_TV },
+ [NM_ATT_POWER_THRESH] = { TLV_TYPE_FIXED, 3 },
+ [NM_ATT_PROB_CAUSE] = { TLV_TYPE_FIXED, 3 },
+ [NM_ATT_RACH_B_THRESH] = { TLV_TYPE_TV },
+ [NM_ATT_LDAVG_SLOTS] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_RAD_SUBC] = { TLV_TYPE_TV },
+ [NM_ATT_RF_MAXPOWR_R] = { TLV_TYPE_TV },
+ [NM_ATT_SITE_INPUTS] = { TLV_TYPE_TL16V },
+ [NM_ATT_SITE_OUTPUTS] = { TLV_TYPE_TL16V },
+ [NM_ATT_SOURCE] = { TLV_TYPE_TL16V },
+ [NM_ATT_SPEC_PROB] = { TLV_TYPE_TV },
+ [NM_ATT_START_TIME] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_T200] = { TLV_TYPE_FIXED, 7 },
+ [NM_ATT_TEI] = { TLV_TYPE_TV },
+ [NM_ATT_TEST_DUR] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_TEST_NO] = { TLV_TYPE_TV },
+ [NM_ATT_TEST_REPORT] = { TLV_TYPE_TL16V },
+ [NM_ATT_VSWR_THRESH] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_WINDOW_SIZE] = { TLV_TYPE_TV },
+ [NM_ATT_TSC] = { TLV_TYPE_TV },
+ [NM_ATT_SW_CONFIG] = { TLV_TYPE_TL16V },
+ [NM_ATT_SEVERITY] = { TLV_TYPE_TV },
+ [NM_ATT_GET_ARI] = { TLV_TYPE_TL16V },
+ [NM_ATT_HW_CONF_CHG] = { TLV_TYPE_TL16V },
+ [NM_ATT_OUTST_ALARM] = { TLV_TYPE_TV },
+ [NM_ATT_MEAS_RES] = { TLV_TYPE_TL16V },
+ },
+};
+
+struct osmobts_trx *get_trx_by_nr(struct osmocom_bts *bts, uint8_t trx_nr)
+{
+ int max = sizeof(bts->trx) / sizeof(bts->trx[0]);
+ struct osmobts_trx *trx;
+
+ if (trx_nr >= max) {
+ LOGP(DOML, LOGL_NOTICE, "Indicated TRX #%d is out of range. (max #%d)\n", trx_nr, max - 1);
+ return NULL;
+ }
+
+ trx = bts->trx[trx_nr];
+ if (!trx) {
+ LOGP(DOML, LOGL_NOTICE, "Indicated TRX #%d does not exist.\n", trx_nr);
+ return NULL;
+ }
+
+ return trx;
+}
+
+struct osmobts_slot *get_slot_by_nr(struct osmobts_trx *trx, uint8_t slot_nr)
+{
+ struct osmobts_slot *slot;
+
+ if (slot_nr >= 8) {
+ LOGP(DOML, LOGL_NOTICE, "Indicated Slot #%d is out of range. (max #8)\n", slot_nr);
+ return NULL;
+ }
+
+ slot = &trx->slot[slot_nr];
+
+ return slot;
+}
+
+static struct msgb *fom_msgb_alloc(void)
+{
+ struct msgb *nmsg;
+
+ nmsg = abis_msgb_alloc(sizeof(struct abis_om_hdr));
+ if (!nmsg)
+ return NULL;
+ return nmsg;
+}
+
+static void fom_push_om(struct msgb *msg, uint8_t mdisc, uint8_t placement, uint8_t sequence)
+{
+ struct abis_om_hdr *noh;
+
+ msg->l3h = msg->data;
+ noh = (struct abis_om_hdr *) msgb_push(msg, sizeof(*noh));
+ noh->mdisc = mdisc;
+ noh->placement = placement;
+ noh->sequence = sequence;
+ noh->length = msgb_l3len(msg);
+}
+
+static int fom_ack_nack(struct ipabis_link *link, struct msgb *msg, uint8_t cause)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ uint8_t *ie;
+
+ /* alter message type */
+ if (cause) {
+ LOGP(DOML, LOGL_NOTICE, "Sending FOM NACK with cause %d.\n", cause);
+ foh->msg_type += 2; /* nack */
+ /* add cause */
+ ie = msgb_put(msg, 2);
+ ie[0] = NM_ATT_NACK_CAUSES;
+ ie[1] = cause;
+ } else {
+ LOGP(DOML, LOGL_NOTICE, "Sending FOM ACK.\n");
+ foh->msg_type++; /* ack */
+ }
+
+ return abis_tx(link, msg);
+}
+
+/*
+ * Formatted O&M messages
+ */
+
+/* 8.3.7 sending SW Activated Report */
+int oml_tx_sw_act_rep(struct ipabis_link *link, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr)
+{
+ struct msgb *nmsg;
+ struct abis_om_fom_hdr *nofh;
+
+ LOGP(DOML, LOGL_INFO, "Sending SW Activated Report (%02x,%02x,%02x).\n", bts_nr, trx_nr, ts_nr);
+
+ nmsg = fom_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ nofh = (struct abis_om_fom_hdr *) msgb_put(nmsg, sizeof(*nofh));
+ nofh->msg_type = NM_MT_SW_ACTIVATED_REP;
+ nofh->obj_class = obj_class;
+ nofh->obj_inst.bts_nr = bts_nr;
+ nofh->obj_inst.trx_nr = trx_nr;
+ nofh->obj_inst.ts_nr = ts_nr;
+ fom_push_om(nmsg, ABIS_OM_MDISC_FOM, ABIS_OM_PLACEMENT_ONLY, 0);
+ abis_push_ipa(nmsg, IPA_PROTO_OML);
+
+ return abis_tx(link, nmsg);
+}
+
+/* 8.8.1 sending State Changed Event Report */
+int oml_tx_state_changed(struct ipabis_link *link, uint8_t op_state, uint8_t avail_status, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr)
+{
+ struct msgb *nmsg;
+ struct abis_om_fom_hdr *nofh;
+ uint8_t *ie;
+
+ LOGP(DOML, LOGL_INFO, "Sending state change (bts=%02x trx=%02x ts=%02x).\n", bts_nr, trx_nr, ts_nr);
+
+ nmsg = fom_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ nofh = (struct abis_om_fom_hdr *) msgb_put(nmsg, sizeof(*nofh));
+ nofh->msg_type = NM_MT_STATECHG_EVENT_REP;
+ nofh->obj_class = obj_class;
+ nofh->obj_inst.bts_nr = bts_nr;
+ nofh->obj_inst.trx_nr = trx_nr;
+ nofh->obj_inst.ts_nr = ts_nr;
+ /* 9.4.38 Operational State */
+ ie = msgb_put(nmsg, 2);
+ ie[0] = NM_ATT_OPER_STATE;
+ ie[1] = op_state;
+ /* 9.4.7 Availability Status */
+ ie = msgb_put(nmsg, 4);
+ ie[0] = NM_ATT_AVAIL_STATUS;
+ ie[1] = 0;
+ ie[2] = 1;
+ ie[3] = avail_status;
+ fom_push_om(nmsg, ABIS_OM_MDISC_FOM, ABIS_OM_PLACEMENT_ONLY, 0);
+ abis_push_ipa(nmsg, IPA_PROTO_OML);
+
+ return abis_tx(link, nmsg);
+}
+
+/* 8.6.1 Set BTS Attributes is received */
+int oml_rx_set_bts_attr(struct osmocom_bts *bts, struct msgb *msg)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ struct tlv_parsed tp;
+ struct bts_support *sup = &bts_support;
+
+ LOGP(DOML, LOGL_INFO, "BSC is setting BTS attributes:\n");
+
+ tlv_parse(&tp, &nm_att_tlvdef, foh->data, msgb_l3len(msg) - sizeof(*foh), 0, 0);
+ /* 9.4.31 Maximum Timing Advance */
+ if (TLVP_PRESENT(&tp, NM_ATT_MAX_TA)) {
+ uint16_t *fn = (uint16_t *) TLVP_VAL(&tp, NM_ATT_START_TIME);
+ bts->max_ta = ntohs(*fn);
+ LOGP(DOML, LOGL_INFO, " Maximum TA = %d\n", bts->max_ta);
+ }
+ /* 9.4.8 BCCH ARFCN */
+ if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN)) {
+ uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_BCCH_ARFCN);
+ uint16_t arfcn = ntohs(*value);
+ LOGP(DOML, LOGL_INFO, " ARFCN = %d", bts->bcch_arfcn);
+ if (arfcn > 1023 || !(sup->freq_map[arfcn >> 3] & (1 << (arfcn & 0x7)))) {
+ LOGP(DOML, LOGL_NOTICE, "Given ARFCN %d is not supported.\n", arfcn);
+ return fom_ack_nack(&bts->link, msg, NM_NACK_FREQ_NOTAVAIL);
+ }
+ bts->bcch_arfcn = arfcn;
+ }
+ /* 9.4.9 BSIC */
+ if (TLVP_PRESENT(&tp, NM_ATT_BSIC)) {
+ uint8_t *bsic = (uint8_t *) TLVP_VAL(&tp, NM_ATT_BSIC);
+ bts->bcc = *bsic & 0x7;
+ bts->ncc = (*bsic >> 3) & 0x7;
+ LOGP(DOML, LOGL_INFO, " BCC = %d\n", bts->bcc);
+ LOGP(DOML, LOGL_INFO, " NCC = %d\n", bts->ncc);
+ }
+ /* 9.4.52 Starting Time */
+ if (TLVP_PRESENT(&tp, NM_ATT_START_TIME)) {
+ uint16_t *fn = (uint16_t *) TLVP_VAL(&tp, NM_ATT_START_TIME);
+ bts->start_time = ntohs(*fn);
+ LOGP(DOML, LOGL_INFO, " Starting Time = %d\n", bts->start_time);
+ }
+
+ return fom_ack_nack(&bts->link, msg, 0);
+}
+
+/* 8.6.2 Set Radio Attributes is received */
+int oml_rx_set_radio_attr(struct osmocom_bts *bts, struct msgb *msg)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ struct tlv_parsed tp;
+ struct osmobts_trx *trx;
+ struct bts_support *sup = &bts_support;
+
+ trx = get_trx_by_nr(bts, foh->obj_inst.trx_nr);
+ if (!trx)
+ return fom_ack_nack(&bts->link, msg, NM_NACK_TRXNR_UNKN);
+
+ LOGP(DOML, LOGL_INFO, "BSC is setting radio attributes:\n");
+
+ tlv_parse(&tp, &nm_att_tlvdef, foh->data, msgb_l3len(msg) - sizeof(*foh), 0, 0);
+ /* 9.4.47 RF Max Power Reduction */
+ if (TLVP_PRESENT(&tp, NM_ATT_RF_MAXPOWR_R)) {
+ trx->rf_red = *TLVP_VAL(&tp, NM_ATT_RF_MAXPOWR_R);
+ LOGP(DOML, LOGL_INFO, " RF Max Power Reduction = %d\n", trx->rf_red);
+ } else
+ trx->rf_red = 0;
+ /* 9.4.5 ARFCN List */
+ if (TLVP_PRESENT(&tp, NM_ATT_ARFCN_LIST)) {
+ uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_ARFCN_LIST);
+ uint16_t length = *(TLVP_VAL(&tp, NM_ATT_ARFCN_LIST) - 1);
+ uint16_t arfcn;
+ int max = (sizeof(trx->arfcn_list) / sizeof(trx->arfcn_list[0]));
+ int i;
+ if (length > max) {
+ LOGP(DOML, LOGL_NOTICE, "Too many ARFCN given. (max #%d)\n", max);
+ return fom_ack_nack(&bts->link, msg, NM_NACK_PARAM_RANGE);
+ }
+ for (i = 0; i < length; i++) {
+ arfcn = ntohs(*value++);
+ if (arfcn > 1023 || !(sup->freq_map[arfcn >> 3] & (1 << (arfcn & 0x7))))
+ return fom_ack_nack(&bts->link, msg, NM_NACK_FREQ_NOTAVAIL);
+ trx->arfcn_list[i] = arfcn;
+ LOGP(DOML, LOGL_INFO, " ARFCN list = %d\n", trx->arfcn_list[i]);
+ }
+ trx->arfcn_num = length;
+ } else
+ trx->arfcn_num = 0;
+
+ return fom_ack_nack(&bts->link, msg, 0);
+}
+
+/* 8.6.3 Set Channel Attributes is received */
+int oml_rx_set_chan_attr(struct osmocom_bts *bts, struct msgb *msg)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ struct tlv_parsed tp;
+ struct osmobts_trx *trx;
+ struct osmobts_slot *slot;
+ struct bts_support *sup = &bts_support;
+
+ trx = get_trx_by_nr(bts, foh->obj_inst.trx_nr);
+ if (!trx)
+ return fom_ack_nack(&bts->link, msg, NM_NACK_TRXNR_UNKN);
+ slot = get_slot_by_nr(trx, foh->obj_inst.ts_nr);
+ if (!slot)
+ return fom_ack_nack(&bts->link, msg, NM_NACK_OBJINST_UNKN);
+
+ LOGP(DOML, LOGL_INFO, "BSC is setting channel attributes:\n");
+
+ tlv_parse(&tp, &nm_att_tlvdef, foh->data, msgb_l3len(msg) - sizeof(*foh), 0, 0);
+ /* 9.4.13 Channel Combination */
+ if (TLVP_PRESENT(&tp, NM_ATT_CHAN_COMB)) {
+ uint8_t comb = *TLVP_VAL(&tp, NM_ATT_CHAN_COMB);
+ if (!sup->chan_comb[comb]) {
+ LOGP(DOML, LOGL_NOTICE, " channel combination %d (not supported).\n", comb);
+ return fom_ack_nack(&bts->link, msg, NM_NACK_SPEC_IMPL_NOTSUPP);
+ }
+ LOGP(DOML, LOGL_INFO, " channel combination = %s\n", bts_support_comb_name(comb));
+ bts_setup_slot(slot, comb);
+ slot->chan_comb = comb;
+ }
+ /* 9.4.21 HSN... */
+ if (TLVP_PRESENT(&tp, NM_ATT_HSN)) {
+ LOGP(DOML, LOGL_NOTICE, "Frequency hopping not supported.\n");
+ return fom_ack_nack(&bts->link, msg, NM_NACK_SPEC_IMPL_NOTSUPP);
+ }
+
+ return fom_ack_nack(&bts->link, msg, 0);
+}
+
+/* 8.9.2 Opstart is received */
+int oml_rx_opstart(struct osmocom_bts *bts, struct msgb *msg)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ struct osmobts_trx *trx;
+ struct osmobts_slot *slot;
+
+
+ /* site manager */
+ if (foh->obj_inst.bts_nr == 0xff) {
+ LOGP(DOML, LOGL_INFO, "BSC is sending Opstart. (Site Manager)\n");
+ oml_tx_state_changed(&bts->link, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, NM_OC_SITE_MANAGER, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, foh->obj_inst.ts_nr);
+ return fom_ack_nack(&bts->link, msg, 0);
+ }
+
+#warning todo: change state
+ /* BTS */
+ if (foh->obj_inst.trx_nr == 0xff) {
+ LOGP(DOML, LOGL_INFO, "BSC is sending Opstart. (BTS)\n");
+ return fom_ack_nack(&bts->link, msg, 0);
+ }
+
+ /* TRX */
+ trx = get_trx_by_nr(bts, foh->obj_inst.trx_nr);
+ if (!trx)
+ return fom_ack_nack(&bts->link, msg, NM_NACK_TRXNR_UNKN);
+ if (foh->obj_inst.ts_nr == 0xff) {
+ LOGP(DOML, LOGL_INFO, "BSC is sending Opstart. (TRX %d)\n", trx->trx_nr);
+ if (trx->link.state == LINK_STATE_IDLE) {
+ int ret;
+
+ /* connecting TRX */
+ ret = abis_open(&trx->link, bts->link.ip);
+ if (ret <= 0) {
+ LOGP(DOML, LOGL_ERROR, "Failed to connect TRX.\n");
+ return fom_ack_nack(&bts->link, msg, NM_NACK_CANT_PERFORM);
+ }
+ }
+ return fom_ack_nack(&bts->link, msg, 0);
+ }
+
+ /* slot */
+ slot = get_slot_by_nr(trx, foh->obj_inst.ts_nr);
+ if (!slot)
+ return fom_ack_nack(&bts->link, msg, NM_NACK_OBJINST_UNKN);
+
+ LOGP(DOML, LOGL_INFO, "BSC is sending Opstart. (trx=%d ts=%d)\n", trx->trx_nr, slot->slot_nr);
+ oml_tx_state_changed(&bts->link, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, NM_OC_CHANNEL, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, foh->obj_inst.ts_nr);
+ return fom_ack_nack(&bts->link, msg, 0);
+}
+
+static int down_fom(struct osmocom_bts *bts, struct msgb *msg)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ int ret;
+
+ if (msgb_l2len(msg) < sizeof(*foh)) {
+ LOGP(DOML, LOGL_NOTICE, "Formatted O&M message too short\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ if (foh->obj_inst.bts_nr != 0 && foh->obj_inst.bts_nr != 0xff) {
+ LOGP(DOML, LOGL_INFO, "Formatted O&M with BTS %d out of range.\n", foh->obj_inst.bts_nr);
+ return fom_ack_nack(&bts->link, msg, NM_NACK_BTSNR_UNKN);
+ }
+
+ switch (foh->msg_type) {
+ case NM_MT_SET_BTS_ATTR:
+ ret = oml_rx_set_bts_attr(bts, msg);
+ break;
+ case NM_MT_SET_RADIO_ATTR:
+ ret = oml_rx_set_radio_attr(bts, msg);
+ break;
+ case NM_MT_SET_CHAN_ATTR:
+ ret = oml_rx_set_chan_attr(bts, msg);
+ break;
+ case NM_MT_OPSTART:
+ ret = oml_rx_opstart(bts, msg);
+ break;
+ case NM_MT_CHG_ADM_STATE:
+ LOGP(DOML, LOGL_INFO, "BSC is changing ADM state.\n");
+ ret = fom_ack_nack(&bts->link, msg, 0);
+ break;
+ default:
+ LOGP(DOML, LOGL_INFO, "unknown Formatted O&M msg_type 0x%02x\n",
+ foh->msg_type);
+ ret = fom_ack_nack(&bts->link, msg, NM_NACK_MSGTYPE_INVAL);
+ }
+
+ return ret;
+}
+
+/*
+ * manufacturer related messages
+ */
+
+static int down_mom(struct osmocom_bts *bts, struct msgb *msg)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ int ret;
+
+ if (msgb_l2len(msg) < sizeof(*foh)) {
+ LOGP(DOML, LOGL_NOTICE, "Manufacturer O&M message too short\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ if (foh->obj_inst.bts_nr != 0 && foh->obj_inst.bts_nr != 0xff) {
+ LOGP(DOML, LOGL_INFO, "Manufacturer O&M with BTS %d out of range.\n", foh->obj_inst.bts_nr);
+ return fom_ack_nack(&bts->link, msg, NM_NACK_BTSNR_UNKN);
+ }
+
+ switch (foh->msg_type) {
+ default:
+ LOGP(DOML, LOGL_INFO, "Manufacturer Formatted O&M msg_type 0x%02x\n",
+ foh->msg_type);
+ ret = fom_ack_nack(&bts->link, msg, NM_NACK_MSGTYPE_INVAL);
+ }
+
+ return ret;
+}
+
+/*
+ * selecting messages
+ */
+
+int down_oml(struct osmocom_bts *bts, struct msgb *msg)
+{
+ struct abis_om_hdr *oh = msgb_l2(msg);
+ int ret = 0;
+
+ if (msgb_l2len(msg) < 1) {
+ LOGP(DOML, LOGL_NOTICE, "OML message too short\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+ msg->l3h = (unsigned char *)oh + sizeof(*oh);
+
+ switch (oh->mdisc) {
+ case ABIS_OM_MDISC_FOM:
+ if (msgb_l2len(msg) < sizeof(*oh)) {
+ LOGP(DOML, LOGL_NOTICE, "Formatted O&M message too short\n");
+ msgb_free(msg);
+ ret = -EIO;
+ break;
+ }
+ ret = down_fom(bts, msg);
+ break;
+ case ABIS_OM_MDISC_MANUF:
+ if (msgb_l2len(msg) < sizeof(*oh)) {
+ LOGP(DOML, LOGL_NOTICE, "Manufacturer O&M message too short\n");
+ msgb_free(msg);
+ ret = -EIO;
+ break;
+ }
+ ret = down_mom(bts, msg);
+ break;
+ default:
+ LOGP(DOML, LOGL_NOTICE, "unknown OML msg_discr 0x%02x\n",
+ oh->mdisc);
+ msgb_free(msg);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+
diff --git a/src/common/rsl.c b/src/common/rsl.c
new file mode 100644
index 00000000..48bb99b2
--- /dev/null
+++ b/src/common/rsl.c
@@ -0,0 +1,787 @@
+/*
+ * (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/*
+ * Radio Link Layer Messages
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/rsl.h>
+#include <osmocore/protocol/gsm_12_21.h>
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/bts/abis.h>
+#include <osmocom/bb/bts/rtp.h>
+#include <osmocom/bb/bts/bts.h>
+#include <osmocom/bb/bts/rsl.h>
+#include <osmocom/bb/bts/oml.h>
+#include <osmocom/bb/bts/support.h>
+
+static int rsl_tx_error_report(struct osmobts_trx *trx, uint8_t cause);
+
+/*
+ * support
+ */
+
+static uint8_t bts_si_list[BTS_SI_NUM] = BTS_SI_LIST;
+
+struct osmobts_lchan *rsl_get_chan(struct osmobts_trx *trx, uint8_t chan_nr)
+{
+ uint8_t ts_nr = chan_nr & 0x07;
+ uint8_t cbits = chan_nr >> 3;
+ struct bts_support *sup = &bts_support;
+ struct osmobts_slot *slot = &trx->slot[ts_nr];
+ struct osmobts_lchan *lchan = NULL;
+
+ if (!slot->tx_ms) {
+ LOGP(DRSL, LOGL_NOTICE, "Given slot not available: %d\n", ts_nr);
+ return NULL;
+ }
+
+ if (cbits == 0x01) {
+ /* TCH/F */
+ if (!sup->chan_comb[NM_CHANC_TCHFull]) {
+ LOGP(DRSL, LOGL_NOTICE, "TCH/F not not supported on slot: %d\n", ts_nr);
+ return NULL;
+ }
+ if (slot->chan_comb != NM_CHANC_TCHFull) {
+ LOGP(DRSL, LOGL_NOTICE, "Given channel type not TCH/F: %d\n", ts_nr);
+ return NULL;
+ }
+ lchan = slot->lchan[0];
+ } else if ((cbits & 0x1e) == 0x02) {
+ /* TCH/H */
+ if (!sup->chan_comb[NM_CHANC_TCHHalf]) {
+ LOGP(DRSL, LOGL_NOTICE, "TCH/H not not supported on slot: %d\n", ts_nr);
+ return NULL;
+ }
+ if (slot->chan_comb != NM_CHANC_TCHHalf) {
+ LOGP(DRSL, LOGL_NOTICE, "Given channel type not TCH/H: %d\n", ts_nr);
+ return NULL;
+ }
+ lchan = slot->lchan[cbits & 0x01];;
+ } else if ((cbits & 0x1c) == 0x04) {
+ /* BCCH+SDCCH4 */
+ if (!sup->chan_comb[NM_CHANC_BCCHComb]) {
+ LOGP(DRSL, LOGL_NOTICE, "Combined BCCH+SDCCH/4 not not supported on slot: %d\n", ts_nr);
+ return NULL;
+ }
+ if (slot->chan_comb != NM_CHANC_BCCHComb) {
+ LOGP(DRSL, LOGL_NOTICE, "Given channel type not Combined BCCH+SDCCH/4: %d\n", ts_nr);
+ return NULL;
+ }
+ lchan = slot->lchan[cbits & 0x03];;
+ } else if ((cbits & 0x18) == 0x08) {
+ /* SDCCH8 */
+ if (!sup->chan_comb[NM_CHANC_SDCCH]) {
+ LOGP(DRSL, LOGL_NOTICE, "SDCCH/8 not not supported on slot: %d\n", ts_nr);
+ return NULL;
+ }
+ if (slot->chan_comb != NM_CHANC_SDCCH) {
+ LOGP(DRSL, LOGL_NOTICE, "Given channel type not SDCCH/8: %d\n", ts_nr);
+ return NULL;
+ }
+ lchan = slot->lchan[cbits & 0x07];
+ } else {
+ LOGP(DRSL, LOGL_NOTICE, "Given chan_nr unknown: %d\n", chan_nr);
+ return NULL;
+ }
+
+ if (!lchan)
+ LOGP(DRSL, LOGL_ERROR, "Lchan not created.\n");
+
+ return lchan;
+}
+
+static struct msgb *rsl_msgb_alloc(int hdr_size)
+{
+ struct msgb *nmsg;
+
+ nmsg = abis_msgb_alloc(hdr_size);
+ if (!nmsg)
+ return NULL;
+ nmsg->l3h = nmsg->data;
+ return nmsg;
+}
+
+static void rsl_trx_push_hdr(struct msgb *msg, uint8_t msg_type)
+{
+ struct abis_rsl_common_hdr *th;
+
+ th = (struct abis_rsl_common_hdr *) msgb_push(msg, sizeof(*th));
+ th->msg_discr = ABIS_RSL_MDISC_TRX;
+ th->msg_type = msg_type;
+}
+
+static void rsl_cch_push_hdr(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr)
+{
+ struct abis_rsl_cchan_hdr *cch;
+
+ cch = (struct abis_rsl_cchan_hdr *) msgb_push(msg, sizeof(*cch));
+ cch->c.msg_discr = ABIS_RSL_MDISC_COM_CHAN;
+ cch->c.msg_type = msg_type;
+ cch->chan_nr = chan_nr;
+}
+
+static void rsl_dch_push_hdr(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr)
+{
+ struct abis_rsl_dchan_hdr *dch;
+
+ dch = (struct abis_rsl_dchan_hdr *) msgb_push(msg, sizeof(*dch));
+ dch->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN;
+ dch->c.msg_type = msg_type;
+ dch->chan_nr = chan_nr;
+}
+
+
+/*
+ * TRX related messages
+ */
+
+/* 8.6.4 sending ERROR REPORT */
+static int rsl_tx_error_report(struct osmobts_trx *trx, uint8_t cause)
+{
+ struct msgb *nmsg;
+ uint8_t *ie;
+
+ LOGP(DRSL, LOGL_NOTICE, "Sending Error Report: cause = 0x%02x\n", cause);
+
+ nmsg = rsl_msgb_alloc(sizeof(struct abis_rsl_common_hdr));
+ if (!nmsg)
+ return -ENOMEM;
+ ie = msgb_put(nmsg, 3);
+ ie[0] = RSL_IE_CAUSE;
+ ie[1] = 1;
+ ie[2] = cause;
+ rsl_trx_push_hdr(nmsg, RSL_MT_ERROR_REPORT);
+ abis_push_ipa(nmsg, IPA_PROTO_RSL);
+
+ return abis_tx(&trx->link, nmsg);
+}
+
+/* 8.6.1 sending RF RESOURCE INDICATION */
+int rsl_tx_rf_res(struct osmobts_trx *trx)
+{
+ struct msgb *nmsg;
+
+ LOGP(DRSL, LOGL_INFO, "Sending RF RESource INDication\n");
+
+ nmsg = rsl_msgb_alloc(sizeof(struct abis_rsl_common_hdr));
+ if (!nmsg)
+ return -ENOMEM;
+ // FIXME: add interference levels of TRX
+ rsl_trx_push_hdr(nmsg, RSL_MT_RF_RES_IND);
+ abis_push_ipa(nmsg, IPA_PROTO_RSL);
+
+ return abis_tx(&trx->link, nmsg);
+}
+
+/*
+ * common channel releated messages
+ */
+
+/* 8.5.1 BCCH INFOrmation is received */
+static int rsl_rx_bcch_info(struct osmobts_trx *trx, struct msgb *msg)
+{
+ struct tlv_parsed tp;
+ uint8_t si;
+ int i;
+
+ LOGP(DRSL, LOGL_INFO, "RSL BCCH Information:\n");
+
+ rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
+
+ /* 9.3.30 System Info Type */
+ if (!TLVP_PRESENT(&tp, RSL_IE_SYSINFO_TYPE)) {
+ return rsl_tx_error_report(trx, RSL_ERR_MAND_IE_ERROR);
+ }
+ si = *TLVP_VAL(&tp, RSL_IE_SYSINFO_TYPE);
+ i = 0;
+ while(i < BTS_SI_NUM) {
+ if (bts_si_list[i] == si)
+ break;
+ i++;
+ }
+ if (i == BTS_SI_NUM) {
+ LOGP(DRSL, LOGL_NOTICE, " SI 0x%02x not supported.\n", si);
+ return rsl_tx_error_report(trx, RSL_ERR_IE_CONTENT);
+ }
+ /* 9.3.39 Full BCCH Information */
+ if (TLVP_PRESENT(&tp, RSL_IE_FULL_BCCH_INFO)) {
+ trx->si.flags[i] |= BTS_SI_USE;
+ memcpy(trx->si.si[i], TLVP_VAL(&tp, RSL_IE_FULL_BCCH_INFO), 23);
+ LOGP(DRSL, LOGL_INFO, " Got new SYSTEM INFORMATION 0x%02x.\n",si);
+ } else if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) {
+ trx->si.flags[i] |= BTS_SI_USE;
+ memcpy(trx->si.si[i], TLVP_VAL(&tp, RSL_IE_L3_INFO), 23);
+ LOGP(DRSL, LOGL_INFO, " Got new SYSTEM INFORMATION 0x%02x.\n",si);
+ } else {
+ trx->si.flags[i] &= ~BTS_SI_USE;
+ LOGP(DRSL, LOGL_INFO, " Removing SYSTEM INFORMATION 0x%02x.\n",si);
+ }
+ trx->si.flags[i] |= BTS_SI_NEW;
+ bts_new_si(trx);
+
+ return 0;
+}
+
+/* 8.5.6 IMMEDIATE ASSIGN COMMAND is received */
+static int rsl_rx_imm_ass(struct osmobts_trx *trx, struct msgb *msg)
+{
+ struct tlv_parsed tp;
+ uint8_t *data;
+
+ LOGP(DRSL, LOGL_INFO, "Immidiate Assignment Command:\n");
+
+ rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
+
+ /* 9.3.30 System Info Type */
+ if (!TLVP_PRESENT(&tp, RSL_IE_FULL_IMM_ASS_INFO)) {
+ return rsl_tx_error_report(trx, RSL_ERR_MAND_IE_ERROR);
+ }
+ data = (uint8_t *) TLVP_VAL(&tp, RSL_IE_FULL_IMM_ASS_INFO);
+ LOGP(DRSL, LOGL_INFO, " length = %d\n", data[-1]);
+
+#warning HACK
+ {
+ struct msgb *nmsg = rsl_msgb_alloc(64);
+ struct l1ctl_info_dl *dl;
+ uint8_t lupd[23] = {0x01,0x03f,0x3d,
+ 0x05,0x08,0x12,0x62,0xf2,0x10,0x31,0x04,0x33,0x05,0xf4,0x87,0x16,0xb3,0xf0,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b};
+
+ memcpy(msgb_put(nmsg, sizeof(lupd)), lupd, sizeof(lupd));
+
+ nmsg->l3h = nmsg->data;
+ dl = (struct l1ctl_info_dl *)(nmsg->l1h = msgb_push(nmsg, sizeof(*dl)));
+ dl->chan_nr = trx->slot[2].lchan[0]->chan_nr;
+ dl->link_id = 0x00;
+ msgb_push(nmsg, sizeof(struct l1ctl_hdr));
+ printf("%p '%s'\n", trx->slot[2].tx_ms, trx->slot[2].tx_ms->ms.name);
+ rx_ph_data_ind(&trx->slot[2].tx_ms->ms, nmsg);
+ }
+
+ return 0;
+}
+
+/*
+ * dedicated channel related messages
+ */
+
+/* 8.4.19 sebdubg RF CHANnel RELease ACKnowledge */
+static int rsl_tx_rf_rel_ack(struct osmobts_trx *trx, struct msgb *msg, uint8_t t1, uint8_t t2, uint8_t t3)
+{
+ struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
+ uint8_t chan_nr = dch->chan_nr;
+
+ LOGP(DRSL, LOGL_NOTICE, "Sending Channel Release ACK\n");
+
+ msg->len = 0;
+ msg->data = msg->tail = msg->l3h;
+
+ rsl_dch_push_hdr(msg, RSL_MT_RF_CHAN_REL_ACK, chan_nr);
+ abis_push_ipa(msg, IPA_PROTO_RSL);
+
+ return abis_tx(&trx->link, msg);
+}
+
+/* 8.4.2 sending CHANnel ACTIVation ACKnowledge */
+static int rsl_tx_chan_ack(struct osmobts_trx *trx, struct msgb *msg, uint8_t t1, uint8_t t2, uint8_t t3)
+{
+ struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
+ uint8_t *ie;
+ uint8_t chan_nr = dch->chan_nr;
+
+ LOGP(DRSL, LOGL_NOTICE, "Sending Channel Activated ACK\n");
+
+ msg->len = 0;
+ msg->data = msg->tail = msg->l3h;
+
+ ie = msgb_put(msg, 3);
+ ie[0] = RSL_IE_FRAME_NUMBER;
+ ie[1] = (t1 << 3) | (t3 >> 3);
+ ie[2] = (t3 & 0x07) | (t2 & 0x1f);
+ rsl_dch_push_hdr(msg, RSL_MT_CHAN_ACTIV_ACK, chan_nr);
+ abis_push_ipa(msg, IPA_PROTO_RSL);
+
+ return abis_tx(&trx->link, msg);
+}
+
+/* 8.4.3 sending CHANnel ACTIVation Negative ACK */
+static int rsl_tx_chan_nack(struct osmobts_trx *trx, struct msgb *msg, uint8_t cause)
+{
+ struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
+ uint8_t *ie;
+ uint8_t chan_nr = dch->chan_nr;
+
+ LOGP(DRSL, LOGL_NOTICE, "Sending Channel Activated NACK: cause = 0x%02x\n", cause);
+
+ msg->len = 0;
+ msg->data = msg->tail = msg->l3h;
+
+ ie = msgb_put(msg, 3);
+ /* 9.3.26 Cause */
+ ie[0] = RSL_IE_CAUSE;
+ ie[1] = 1;
+ ie[2] = cause;
+ rsl_dch_push_hdr(msg, RSL_MT_CHAN_ACTIV_NACK, chan_nr);
+ abis_push_ipa(msg, IPA_PROTO_RSL);
+
+ return abis_tx(&trx->link, msg);
+}
+
+/* 8.5.3 sending CHANnel ReQuireD */
+int rsl_tx_chan_rqd(struct osmobts_trx *trx)
+{
+ struct msgb *nmsg;
+ uint8_t *ie;
+
+ LOGP(DRSL, LOGL_NOTICE, "Sending Channel Required\n");
+
+ nmsg = rsl_msgb_alloc(sizeof(struct abis_rsl_cchan_hdr));
+ if (!nmsg)
+ return -ENOMEM;
+ ie = msgb_put(nmsg, 4);
+ /* 9.3.19 Request Reference */
+ ie[0] = RSL_IE_REQ_REFERENCE;
+ ie[1] = 0xe0; // FIXME
+ ie[2] = 0x00;
+ ie[3] = 0x00;
+ /* 9.3.17 Access Delay */
+ ie = msgb_put(nmsg, 2);
+ ie[0] = RSL_IE_ACCESS_DELAY;
+ ie[1] = 0x00; // FIXME
+ rsl_cch_push_hdr(nmsg, RSL_MT_CHAN_RQD, 0x88); // FIXME
+ abis_push_ipa(nmsg, IPA_PROTO_RSL);
+
+ return abis_tx(&trx->link, nmsg);
+}
+
+/* 8.4.1 CHANnel ACTIVation is received */
+static int rsl_rx_chan_activ(struct osmobts_trx *trx, struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
+ struct tlv_parsed tp;
+ uint8_t type, mode;
+ struct osmobts_lchan *lchan;
+
+ LOGP(DRSL, LOGL_INFO, "Channel Activation:\n");
+
+ lchan = rsl_get_chan(trx, dch->chan_nr);
+ if (!lchan)
+ return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR);
+
+ rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
+
+ /* 9.3.3 Activation Type */
+ if (!TLVP_PRESENT(&tp, RSL_IE_ACT_TYPE)) {
+ LOGP(DRSL, LOGL_NOTICE, "missing Activation Type\n");
+ msgb_free(msg);
+ return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR);
+ }
+ type = *TLVP_VAL(&tp, RSL_IE_ACT_TYPE);
+ /* 9.3.6 Channel Mode */
+ if (!TLVP_PRESENT(&tp, RSL_IE_CHAN_MODE)) {
+ LOGP(DRSL, LOGL_NOTICE, "missing Channel Mode\n");
+ msgb_free(msg);
+ return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR);
+ }
+ mode = *TLVP_VAL(&tp, RSL_IE_CHAN_MODE);
+
+ LOGP(DRSL, LOGL_INFO, " chan_nr=0x%02x type=0x%02x mode=0x%02x\n", dch->chan_nr, type, mode);
+
+ return rsl_tx_chan_ack(trx, msg, 0, 0, 0);
+}
+
+/* 8.4.14 RF CHANnel RELease is received */
+static int rsl_rx_rf_chan_rel(struct osmobts_trx *trx, struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
+ struct osmobts_lchan *lchan;
+ int rc;
+
+ LOGP(DRSL, LOGL_INFO, "Channel Release:\n");
+
+ lchan = rsl_get_chan(trx, dch->chan_nr);
+ if (!lchan)
+ return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR);
+
+ LOGP(DRSL, LOGL_INFO, " chan_nr=0x%02x\n", dch->chan_nr);
+
+ lapdm_reset(&lchan->l2_entity.lapdm_dcch);
+ lapdm_reset(&lchan->l2_entity.lapdm_acch);
+
+ rc = rsl_tx_rf_rel_ack(trx, msg, 0, 0, 0);
+
+ if (lchan->rtp.socket_created)
+ rsl_tx_ipac_dlcx_ind(lchan, RSL_ERR_NORMAL_UNSPEC);
+
+ return rc;
+}
+
+/*
+ * ip.access related messages
+ */
+
+int rsl_tx_ipac_dlcx_ind(struct osmobts_lchan *lchan, uint8_t cause)
+{
+ struct msgb *nmsg;
+ struct osmobts_trx *trx = lchan->slot->trx;
+ uint8_t *ie;
+
+ LOGP(DRSL, LOGL_NOTICE, "Sending RTP delete indication: cause=%d\n", cause);
+
+ nmsg = rsl_msgb_alloc(sizeof(struct abis_rsl_common_hdr));
+ if (!nmsg)
+ return -ENOMEM;
+ ie = msgb_put(nmsg, 3);
+ ie[0] = RSL_IE_CAUSE;
+ ie[1] = 1;
+ ie[2] = cause;
+ rsl_trx_push_hdr(nmsg, RSL_MT_IPAC_DLCX_IND);
+ abis_push_ipa(nmsg, IPA_PROTO_RSL);
+
+ return abis_tx(&trx->link, nmsg);
+}
+
+static int rsl_tx_ipac_cx_ack(struct osmobts_trx *trx, struct msgb *msg, uint32_t ip, uint16_t port, uint16_t *conn_id)
+{
+ struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
+ uint8_t chan_nr = dch->chan_nr;
+ uint8_t msg_type = dch->c.msg_type;
+ uint8_t *ie;
+
+ LOGP(DRSL, LOGL_NOTICE, "Sending RTP connection request ACK\n");
+
+ msg->len = 0;
+ msg->data = msg->tail = msg->l3h;
+
+ if (conn_id)
+ msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, htons(*conn_id));
+ ie = msgb_put(msg, 5);
+ ie[0] = RSL_IE_IPAC_LOCAL_IP;
+ *((uint32_t *)(ie + 1)) = htonl(ip);
+ msgb_tv16_put(msg, RSL_IE_IPAC_LOCAL_PORT, htons(port));
+
+ rsl_dch_push_hdr(msg, msg_type + 1, chan_nr);
+ abis_push_ipa(msg, IPA_PROTO_RSL);
+
+ return abis_tx(&trx->link, msg);
+}
+
+static int rsl_tx_ipac_cx_nack(struct osmobts_trx *trx, struct msgb *msg, uint8_t cause)
+{
+ struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
+ uint8_t chan_nr = dch->chan_nr;
+ uint8_t msg_type = dch->c.msg_type;
+ uint8_t *ie;
+
+ LOGP(DRSL, LOGL_NOTICE, "Sending RTP connection request NACK: cause=%d\n", cause);
+
+ msg->len = 0;
+ msg->data = msg->tail = msg->l3h;
+
+ ie = msgb_put(msg, 3);
+ /* 9.3.26 Cause */
+ ie[0] = RSL_IE_CAUSE;
+ ie[1] = 1;
+ ie[2] = cause;
+ rsl_dch_push_hdr(msg, msg_type + 2, chan_nr);
+ abis_push_ipa(msg, IPA_PROTO_RSL);
+
+ return abis_tx(&trx->link, msg);
+}
+
+static int rsl_rx_ipac_crcx_mdcx(struct osmobts_trx *trx, struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
+ struct tlv_parsed tp;
+ struct osmobts_lchan *lchan;
+ uint32_t *ip = NULL;
+ uint16_t *port = NULL;
+ uint8_t *speech_mode = NULL, *payload_type = NULL;
+ uint16_t conn_id;
+ int rc;
+
+ if (dch->c.msg_type == RSL_MT_IPAC_CRCX)
+ LOGP(DRSL, LOGL_INFO, "Request of creating RTP connection:\n");
+ else
+ LOGP(DRSL, LOGL_INFO, "Request of modding RTP connection:\n");
+
+ lchan = rsl_get_chan(trx, dch->chan_nr);
+ if (!lchan)
+ return rsl_tx_ipac_cx_nack(trx, msg, RSL_ERR_MAND_IE_ERROR);
+
+ rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
+
+ if (TLVP_PRESENT(&tp, RSL_IE_IPAC_SPEECH_MODE)) {
+ speech_mode = (uint8_t *) TLVP_VAL(&tp, RSL_IE_IPAC_SPEECH_MODE);
+ }
+ if (TLVP_PRESENT(&tp, RSL_IE_IPAC_RTP_PAYLOAD)) {
+ payload_type = (uint8_t *) TLVP_VAL(&tp, RSL_IE_IPAC_RTP_PAYLOAD);
+ }
+ if (TLVP_PRESENT(&tp, RSL_IE_IPAC_REMOTE_IP)) {
+ ip = (uint32_t *) TLVP_VAL(&tp, RSL_IE_IPAC_REMOTE_IP);
+ }
+ if (TLVP_PRESENT(&tp, RSL_IE_IPAC_REMOTE_PORT)) {
+ port = (uint16_t *) TLVP_VAL(&tp, RSL_IE_IPAC_REMOTE_PORT);
+ }
+
+ if (!lchan->rtp.socket_created) {
+ if (dch->c.msg_type == RSL_MT_IPAC_CRCX && !payload_type) {
+ LOGP(DRSL, LOGL_ERROR, "Missing payload type IE.\n");
+ return rsl_tx_ipac_cx_nack(trx, msg, RSL_ERR_MAND_IE_ERROR);
+ }
+ rc = rtp_create_socket(lchan, &lchan->rtp);
+ if (rc < 0) {
+ LOGP(DRSL, LOGL_ERROR, "Failed to create RTP/RTCP sockets.\n");
+ return rsl_tx_ipac_cx_nack(trx, msg, RSL_ERR_RES_UNAVAIL);
+ }
+
+ rc = rtp_bind_socket(&lchan->rtp);
+ if (rc < 0) {
+ LOGP(DRSL, LOGL_ERROR, "Failed to bind RTP/RTCP sockets.\n");
+ rtp_close_socket(&lchan->rtp);
+ return rsl_tx_ipac_cx_nack(trx, msg, RSL_ERR_RES_UNAVAIL);
+ }
+ }
+ if (payload_type)
+ lchan->rtp.payload_type = *payload_type;
+
+ if (ip && port) {
+ rc = rtp_connect_socket(&lchan->rtp, ntohl(*ip), ntohs(*port));
+ if (rc < 0) {
+ LOGP(DRSL, LOGL_ERROR, "Failed to connect RTP/RTCP sockets.\n");
+ rtp_close_socket(&lchan->rtp);
+ return rsl_tx_ipac_cx_nack(trx, msg, RSL_ERR_RES_UNAVAIL);
+ }
+ }
+
+ conn_id = 0; // FIXME: what the hack is conn_id for?
+ return rsl_tx_ipac_cx_ack(trx, msg, ntohl(lchan->rtp.rtp_udp.sin_local.sin_addr.s_addr), ntohs(lchan->rtp.rtp_udp.sin_local.sin_port), &conn_id);
+}
+
+static int rsl_rx_ipac_dlcx(struct osmobts_trx *trx, struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
+ struct tlv_parsed tp;
+ struct osmobts_lchan *lchan;
+
+ LOGP(DRSL, LOGL_INFO, "Request of deleting RTP connection:\n");
+
+ lchan = rsl_get_chan(trx, dch->chan_nr);
+ if (!lchan)
+ return rsl_tx_ipac_cx_nack(trx, msg, RSL_ERR_MAND_IE_ERROR);
+
+ rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
+
+ rtp_close_socket(&lchan->rtp);
+
+ return rsl_tx_ipac_cx_ack(trx, msg, ntohl(lchan->rtp.rtp_udp.sin_local.sin_addr.s_addr), ntohs(lchan->rtp.rtp_udp.sin_local.sin_port), NULL);
+}
+
+/*
+ * selecting message
+ */
+
+static int rsl_rx_rll(struct osmobts_trx *trx, struct msgb *msg)
+{
+ struct abis_rsl_rll_hdr *rh = msgb_l2(msg);
+ struct osmobts_lchan *lchan;
+
+ if (msgb_l2len(msg) < sizeof(*rh)) {
+ LOGP(DRSL, LOGL_NOTICE, "RSL Radio Link Layer message too short\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+ msg->l3h = (unsigned char *)rh + sizeof(*rh);
+
+ lchan = rsl_get_chan(trx, rh->chan_nr);
+ if (!lchan)
+ return rsl_tx_chan_nack(trx, msg, RSL_ERR_MAND_IE_ERROR);
+
+ return rslms_recvmsg(msg, &lchan->l2_entity);
+}
+
+int rsl_tx_rll(struct msgb *msg, struct osmol2_entity *l2_entity)
+{
+ struct osmobts_lchan *lchan = container_of(l2_entity, struct osmobts_lchan, l2_entity);
+ struct abis_rsl_common_hdr *rh = msgb_l2(msg);
+
+ LOGP(DRSL, LOGL_INFO, "Got '%s' message from L2.\n", get_rsl_name(rh->msg_type));
+
+ abis_push_ipa(msg, IPA_PROTO_RSL);
+
+ return abis_tx(&lchan->slot->trx->link, msg);
+}
+
+static int rsl_rx_cchan(struct osmobts_trx *trx, struct msgb *msg)
+{
+ struct abis_rsl_cchan_hdr *cch = msgb_l2(msg);
+ int ret = 0;
+
+ if (msgb_l2len(msg) < sizeof(*cch)) {
+ LOGP(DRSL, LOGL_NOTICE, "RSL Common Channel Management message too short\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+ msg->l3h = (unsigned char *)cch + sizeof(*cch);
+
+ switch (cch->c.msg_type) {
+ case RSL_MT_BCCH_INFO:
+ ret = rsl_rx_bcch_info(trx, msg);
+ break;
+ case RSL_MT_IMMEDIATE_ASSIGN_CMD:
+ ret = rsl_rx_imm_ass(trx, msg);
+ break;
+ default:
+ LOGP(DRSL, LOGL_NOTICE, "unsupported RSL cchan msg_type 0x%02x\n",
+ cch->c.msg_type);
+ ret = -EINVAL;
+ }
+
+ msgb_free(msg);
+ return ret;
+}
+
+static int rsl_rx_dchan(struct osmobts_trx *trx, struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
+ int ret = 0;
+
+ if (msgb_l2len(msg) < sizeof(*dch)) {
+ LOGP(DRSL, LOGL_NOTICE, "RSL Dedicated Channel Management message too short\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+ msg->l3h = (unsigned char *)dch + sizeof(*dch);
+
+ switch (dch->c.msg_type) {
+ case RSL_MT_CHAN_ACTIV:
+ return rsl_rx_chan_activ(trx, msg);
+ case RSL_MT_RF_CHAN_REL:
+ return rsl_rx_rf_chan_rel(trx, msg);
+ default:
+ LOGP(DRSL, LOGL_NOTICE, "unsupported RSL dchan msg_type 0x%02x\n",
+ dch->c.msg_type);
+ ret = -EINVAL;
+ }
+
+ msgb_free(msg);
+ return ret;
+}
+
+static int rsl_rx_trx(struct osmobts_trx *trx, struct msgb *msg)
+{
+ struct abis_rsl_common_hdr *th = msgb_l2(msg);
+ int ret = 0;
+
+ if (msgb_l2len(msg) < sizeof(*th)) {
+ LOGP(DRSL, LOGL_NOTICE, "RSL TRX message too short\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+ msg->l3h = (unsigned char *)th + sizeof(*th);
+
+ switch (th->msg_type) {
+ case RSL_MT_SACCH_FILL:
+ ret = rsl_rx_bcch_info(trx, msg);
+ break;
+ default:
+ LOGP(DRSL, LOGL_NOTICE, "unsupported RSL TRX msg_type 0x%02x\n",
+ th->msg_type);
+ ret = -EINVAL;
+ }
+
+ msgb_free(msg);
+ return ret;
+}
+
+static int rsl_rx_ipaccess(struct osmobts_trx *trx, struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
+ int ret = 0;
+
+ if (msgb_l2len(msg) < sizeof(*dch)) {
+ LOGP(DRSL, LOGL_NOTICE, "RSL ip.access message too short\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+ msg->l3h = (unsigned char *)dch + sizeof(*dch);
+
+ switch (dch->c.msg_type) {
+ case RSL_MT_IPAC_CRCX:
+ case RSL_MT_IPAC_MDCX:
+ return rsl_rx_ipac_crcx_mdcx(trx, msg);
+ case RSL_MT_IPAC_DLCX:
+ return rsl_rx_ipac_dlcx(trx, msg);
+ default:
+ LOGP(DRSL, LOGL_NOTICE, "unsupported RSL ip.access msg_type 0x%02x\n",
+ dch->c.msg_type);
+ ret = -EINVAL;
+ }
+
+ msgb_free(msg);
+ return ret;
+}
+
+int down_rsl(struct osmobts_trx *trx, struct msgb *msg)
+{
+ struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
+ int ret = 0;
+
+ if (msgb_l2len(msg) < sizeof(*rslh)) {
+ LOGP(DRSL, LOGL_NOTICE, "RSL message too short\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ switch (rslh->msg_discr & 0xfe) {
+ case ABIS_RSL_MDISC_RLL:
+ ret = rsl_rx_rll(trx, msg);
+ break;
+ case ABIS_RSL_MDISC_COM_CHAN:
+ ret = rsl_rx_cchan(trx, msg);
+ break;
+ case ABIS_RSL_MDISC_DED_CHAN:
+ ret = rsl_rx_dchan(trx, msg);
+ break;
+ case ABIS_RSL_MDISC_TRX:
+ ret = rsl_rx_trx(trx, msg);
+ break;
+ case ABIS_RSL_MDISC_IPACCESS:
+ ret = rsl_rx_ipaccess(trx, msg);
+ break;
+ default:
+ LOGP(DRSL, LOGL_NOTICE, "unknown RSL msg_discr 0x%02x\n",
+ rslh->msg_discr);
+ msgb_free(msg);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+
diff --git a/src/common/rtp.c b/src/common/rtp.c
new file mode 100644
index 00000000..64c51aa5
--- /dev/null
+++ b/src/common/rtp.c
@@ -0,0 +1,517 @@
+/*
+ * (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/*
+ * RTP peer
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/bts/support.h>
+#include <osmocom/bb/bts/abis.h>
+#include <osmocom/bb/bts/rtp.h>
+#include <osmocom/bb/bts/bts.h>
+#include <osmocom/bb/bts/rsl.h>
+#include <osmocom/bb/bts/oml.h>
+
+#define RTP_PORTBASE 30000
+static unsigned short next_udp_port = RTP_PORTBASE;
+
+enum {
+ UDP_PRIV_RTP,
+ UDP_PRIV_RTCP,
+};
+
+#define RTP_ALLOC_SIZE 1500
+
+/* according to RFC 1889 */
+struct rtcp_hdr {
+ u_int8_t byte0;
+ u_int8_t type;
+ u_int16_t length;
+} __attribute__((packed));
+
+#define RTCP_TYPE_SDES 202
+
+#define RTCP_IE_CNAME 1
+
+/* according to RFC 3550 */
+struct rtp_hdr {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ u_int8_t csrc_count:4,
+ extension:1,
+ padding:1,
+ version:2;
+ u_int8_t payload_type:7,
+ marker:1;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ u_int8_t version:2,
+ padding:1,
+ extension:1,
+ csrc_count:4;
+ u_int8_t marker:1,
+ payload_type:7;
+#endif
+ u_int16_t sequence;
+ u_int32_t timestamp;
+ u_int32_t ssrc;
+
+ uint8_t data[0];
+} __attribute__((packed));
+
+struct rtp_x_hdr {
+ u_int16_t by_profile;
+ u_int16_t length;
+} __attribute__((packed));
+
+#define RTP_VERSION 2
+
+int rtp_tx_rtp(struct osmobts_rtp *rtp, struct msgb *msg)
+{
+ struct gsm_data_frame *frame = (struct gsm_data_frame *) msg->data;
+ int payload_len = msg->len - sizeof(*frame);
+ uint8_t *payload = frame->data;
+ struct msgb *nmsg = NULL;
+ int duration = 0;
+ struct rtp_hdr *nrtph;
+
+ if (rtp->rtp_udp.bfd.fd <= 0) {
+ LOGP(DRTP, LOGL_INFO, "Socket already closed, dropping\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ switch(rtp->payload_type) {
+ case RTP_PT_GSM_FULL:
+ nmsg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL");
+ duration = 160;
+ break;
+ case RTP_PT_GSM_EFR:
+ nmsg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL");
+ duration = 160;
+ break;
+ }
+ if (!msg) {
+ msgb_free(msg);
+ return -ENOMEM;
+ }
+
+ nrtph = (struct rtp_hdr *) msgb_put(nmsg, sizeof(*nrtph) + payload_len);
+ nrtph->version = RTP_VERSION;
+ nrtph->padding = 0;
+ nrtph->extension = 0;
+ nrtph->csrc_count = 0;
+ nrtph->marker = 0;
+ nrtph->payload_type = rtp->payload_type;
+ nrtph->sequence = rtp->sequence++;
+ nrtph->timestamp = rtp->timestamp;
+ rtp->timestamp += duration;
+ nrtph->ssrc = rtp->ssrc;
+ memcpy(nrtph->data, payload, payload_len);
+ msgb_enqueue(&rtp->rtp_udp.tx_queue, nmsg);
+ rtp->rtp_udp.bfd.when |= BSC_FD_WRITE;
+
+ msgb_free(msg);
+
+ return 0;
+}
+
+/* received TCH frame */
+static int rtp_rx_rtp(struct osmobts_rtp *rtp, struct msgb *msg)
+{
+ struct rtp_hdr *rtph = (struct rtp_hdr *) msg->data;
+ struct rtp_x_hdr *rtpxh;
+ uint8_t *payload;
+ int payload_len, x_len;
+ struct msgb *nmsg = NULL;
+ struct gsm_data_frame *frame;
+ time_t now;
+
+ if (msg->len < 12) {
+ LOGP(DRTP, LOGL_NOTICE, "RTP packet too short\n");
+ return -EINVAL;
+ }
+ if (rtph->version != RTP_VERSION) {
+ LOGP(DRTP, LOGL_NOTICE, "RTP version missmatch.\n");
+ return -EINVAL;
+ }
+ payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
+ payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
+ if (payload_len < 0) {
+ LOGP(DRTP, LOGL_NOTICE, "RTP payload too short.\n");
+ return -EINVAL;
+ }
+ if (rtph->extension) {
+ if (payload_len < sizeof(struct rtp_x_hdr)) {
+ LOGP(DRTP, LOGL_NOTICE, "RTP frame for extension too short.\n");
+ return -EINVAL;
+ }
+ rtpxh = (struct rtp_x_hdr *)payload;
+ x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
+ payload += x_len;
+ payload_len -= x_len;
+ if (payload_len < 0) {
+ LOGP(DRTP, LOGL_NOTICE, "RTP extension exceeds frame length.\n");
+ return -EINVAL;
+ }
+ }
+ if (rtph->padding) {
+ if (payload_len == 0) {
+ LOGP(DRTP, LOGL_NOTICE, "RTP frame too short for padding.\n");
+ return -EINVAL;
+ }
+ payload_len -= payload[payload_len - 1];
+ if (payload_len < 0) {
+ LOGP(DRTP, LOGL_NOTICE, "RTP frame has padding greater than payload.\n");
+ return -EINVAL;
+ }
+ }
+ switch (rtph->payload_type) {
+ case RTP_PT_GSM_FULL:
+ if (payload_len != 33) {
+ LOGP(DRTP, LOGL_NOTICE, "RTP full rate frame has invald payload length.\n");
+ }
+ nmsg = msgb_alloc(sizeof(*frame) + payload_len, "GSM-FULL");
+ break;
+ if (payload_len != 31) {
+ LOGP(DRTP, LOGL_NOTICE, "RTP enhanced full rate frame has invald payload length.\n");
+ }
+ nmsg = msgb_alloc(sizeof(*frame) + payload_len, "GSM-EFR");
+ break;
+ }
+ if (!nmsg)
+ return -ENOMEM;
+ frame = (struct gsm_data_frame *) msgb_put(nmsg, sizeof(*frame) + payload_len);
+ frame->payload_type = rtph->payload_type;
+ frame->timestamp = ntohl(rtph->timestamp);
+ memcpy(frame->data, payload, payload_len);
+
+ /* check jitterbuffer and adjust delay */
+ time(&now);
+ if (rtp->dejitter_check <= now) {
+ struct msgb *fmsg;
+
+ LOGP(DRTP, LOGL_INFO, "RTP dejitter: minimum fill: %d packets (dropping)\n", rtp->dejitter_min);
+ while (rtp->dejitter_min && (fmsg = msgb_dequeue(&rtp->dejitter_queue))) {
+ msgb_free(fmsg);
+ rtp->dejitter_num--;
+ rtp->dejitter_min--;
+ }
+ rtp->dejitter_check = now + 5;
+ rtp->dejitter_min = 9999;
+ }
+ if (rtp->dejitter_num < rtp->dejitter_min)
+ rtp->dejitter_min = rtp->dejitter_num;
+
+ /* write to dejitter queue */
+ msgb_enqueue(&rtp->dejitter_queue, nmsg);
+ rtp->dejitter_num++;
+
+ if (rtp->voice_req)
+ return 0;
+
+ // FIXME: send voice frame to l1ctl
+ rtp->voice_req = 1;
+ rtp->dejitter_num--;
+ if (rtp->last_frame)
+ msgb_free(rtp->last_frame);
+ rtp->last_frame = nmsg;
+
+ return 0;
+}
+
+/* on confirm, send next packet */
+int rtp_voice_conf(struct osmobts_rtp *rtp)
+{
+ struct msgb *msg;
+
+
+ msg = msgb_dequeue(&rtp->dejitter_queue);
+ if (!msg) {
+ msg = rtp->last_frame;
+ if (!msg) {
+ rtp->voice_req = 0;
+ return 0;
+ }
+ } else {
+ rtp->dejitter_num--;
+ if (rtp->last_frame)
+ msgb_free(rtp->last_frame);
+ rtp->last_frame = msg;
+ }
+
+ // FIXME: send voice frame to l1ctl
+
+ return 0;
+}
+
+static int udp_bfd_cb(struct bsc_fd *bfd, unsigned int flags)
+{
+ struct osmobts_udp *udp = bfd->data;
+ struct msgb *msg = NULL;
+ int rc;
+
+ switch (bfd->priv_nr) {
+ case UDP_PRIV_RTP:
+ msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP");
+ break;
+ case UDP_PRIV_RTCP:
+ msg = msgb_alloc(RTP_ALLOC_SIZE, "RTCP");
+ break;
+ }
+ if (!msg)
+ return -ENOMEM;
+
+ rc = read(bfd->fd, msg->data, RTP_ALLOC_SIZE);
+ if (rc <= 0) {
+ bfd->when &= ~BSC_FD_READ;
+ return rc;
+ }
+ msgb_put(msg, rc);
+
+ switch (bfd->priv_nr) {
+ case UDP_PRIV_RTP:
+ if ((flags & BSC_FD_READ))
+ rc = rtp_rx_rtp(udp->rtp, msg);
+ if ((flags & BSC_FD_WRITE)) {
+ struct msgb *msg = msgb_dequeue(&udp->tx_queue);
+
+ if (!msg) {
+ bfd->when &= ~BSC_FD_WRITE;
+ rc = 0;
+ break;
+ }
+ rc = write(bfd->fd, msg->data, msg->len);
+ }
+ break;
+ case UDP_PRIV_RTCP:
+ // FIXME: implement RTCP
+ rc = 0;
+ break;
+ }
+ msgb_free(msg);
+
+ return rc;
+}
+
+/* subfunction: create an UDP socket */
+static int udp_create_socket(struct osmobts_rtp *rtp, struct osmobts_udp *udp, int priv_nr)
+{
+ int rc;
+
+ if (udp->bfd.fd <= 0)
+ return -EBUSY;
+
+ rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (rc < 0)
+ return rc;
+ udp->bfd.fd = rc;
+ udp->bfd.data = udp;
+ udp->bfd.priv_nr = priv_nr;
+ udp->bfd.cb = udp_bfd_cb;
+ udp->bfd.when = BSC_FD_READ;
+
+ udp->rtp = rtp;
+ rc = bsc_register_fd(&udp->bfd);
+ if (rc < 0) {
+ close(udp->bfd.fd);
+ udp->bfd.fd = 0;
+ }
+ INIT_LLIST_HEAD(&udp->tx_queue);
+
+ return udp->bfd.fd;
+}
+
+/* subfunction: close an UDP socket */
+static void udp_close_socket(struct osmobts_udp *udp)
+{
+ struct msgb *msg;
+
+ if (udp->bfd.fd <= 0)
+ return;
+
+ while ((msg = msgb_dequeue(&udp->tx_queue)))
+ msgb_free(msg);
+
+ bsc_unregister_fd(&udp->bfd);
+ close(udp->bfd.fd);
+ udp->bfd.fd = 0;
+}
+
+/* create RTP/RTCP sockets */
+int rtp_create_socket(struct osmobts_lchan *lchan, struct osmobts_rtp *rtp)
+{
+ int rc;
+
+ LOGP(DRTP, LOGL_INFO, "Creating RTP/RTCP sockets\n");
+
+ rc = udp_create_socket(rtp, &rtp->rtp_udp, UDP_PRIV_RTP);
+ if (rc < 0) {
+ LOGP(DRTP, LOGL_ERROR, "Failed to create RTP socket\n");
+ return rc;
+ }
+ rc = udp_create_socket(rtp, &rtp->rtcp_udp, UDP_PRIV_RTCP);
+ if (rc < 0) {
+ LOGP(DRTP, LOGL_ERROR, "Failed to create RTCP socket\n");
+ udp_close_socket(&rtp->rtp_udp);
+ return rc;
+ }
+ rtp->socket_created = 1;
+ rtp->lchan = lchan;
+ rtp->ssrc = rand();
+ rtp->sequence = random();
+ rtp->timestamp = random();
+ INIT_LLIST_HEAD(&rtp->dejitter_queue);
+ rtp->dejitter_num = 0;
+ rtp->dejitter_check = 0;
+
+ return 0;
+}
+
+/* close RTP/RTCP sockets */
+int rtp_close_socket(struct osmobts_rtp *rtp)
+{
+ struct msgb *msg;
+
+ LOGP(DRTP, LOGL_INFO, "Closing RTP/RTCP sockets\n");
+
+ while ((msg = msgb_dequeue(&rtp->dejitter_queue)))
+ msgb_free(msg);
+ if (rtp->last_frame) {
+ msgb_free(rtp->last_frame);
+ rtp->last_frame = NULL;
+ }
+
+ udp_close_socket(&rtp->rtp_udp);
+ udp_close_socket(&rtp->rtcp_udp);
+ rtp->socket_created = 0;
+
+ return 0;
+}
+
+/* subfunction: bind an UDP socket */
+static int udp_bind_socket(struct osmobts_udp *udp)
+{
+ socklen_t alen = sizeof(udp->sin_local);
+ int rc;
+
+ if (udp->bfd.fd <= 0)
+ return -EINVAL;
+
+ udp->sin_local.sin_family = AF_INET;
+ udp->sin_local.sin_addr.s_addr = INADDR_ANY;
+ udp->sin_local.sin_port = htons(next_udp_port++ & 0xffff);
+
+ rc = bind(udp->bfd.fd, (struct sockaddr *)&udp->sin_local, sizeof(udp->sin_local));
+ if (rc < 0)
+ return rc;
+
+ rc = getsockname(udp->bfd.fd, (struct sockaddr *)&udp->sin_local, &alen);
+
+ return 0;
+}
+
+/* binds the local RTP/RTCP sockets */
+int rtp_bind_socket(struct osmobts_rtp *rtp)
+{
+ int rc;
+ int i;
+
+ LOGP(DRTP, LOGL_INFO, "Binding RTP/RTCP sockets\n");
+
+ if (!rtp->socket_created)
+ return -EINVAL;
+
+ for (i = 0; i < 1000; i++) {
+ /* try RTP socket */
+ rc = udp_bind_socket(&rtp->rtp_udp);
+ if (rc < 0)
+ continue;
+ /* now try RTCP socket */
+ rc = udp_bind_socket(&rtp->rtp_udp);
+ if (rc == 0) {
+ LOGP(DRTP, LOGL_INFO, "Sockets bount to: port(RTP)=%d port(RTCP)=%d\n", ntohs(rtp->rtp_udp.sin_local.sin_port), ntohs(rtp->rtcp_udp.sin_local.sin_port));
+ return 0;
+ }
+ /* if fails, create a new RTP socket and start over */
+ udp_close_socket(&rtp->rtp_udp);
+ rc = udp_create_socket(rtp, &rtp->rtp_udp, UDP_PRIV_RTP);
+ if (rc < 0)
+ return rc;
+ }
+
+ return -EIO;
+}
+
+
+/* subfunction: connect an UDP socket */
+static int udp_connect_socket(struct osmobts_udp *udp, uint32_t ip, uint16_t port)
+{
+ socklen_t alen = sizeof(udp->sin_local);
+ int rc;
+
+ if (udp->bfd.fd <= 0)
+ return -EINVAL;
+
+ udp->sin_remote.sin_family = AF_INET;
+ udp->sin_remote.sin_addr.s_addr = htonl(ip);
+ udp->sin_remote.sin_port = htons(port);
+
+ rc = connect(udp->bfd.fd, (struct sockaddr *)&udp->sin_remote, sizeof(udp->sin_remote));
+ if (rc < 0)
+ return rc;
+
+ rc = getsockname(udp->bfd.fd, (struct sockaddr *)&udp->sin_local, &alen);
+
+ return 0;
+}
+
+/* connects the local RTP/RTCP sockets */
+int rtp_connect_socket(struct osmobts_rtp *rtp, uint32_t ip, uint16_t port)
+{
+ int rc;
+
+ LOGP(DRTP, LOGL_INFO, "Binding RTP/RTCP sockets\n");
+
+ rc = udp_connect_socket(&rtp->rtp_udp, ip, port);
+ if (rc < 0) {
+ LOGP(DRTP, LOGL_ERROR, "Failed to connect RTP socket: ip=%08x, port=%d\n", ip, port);
+ return rc;
+ }
+
+ rc = udp_connect_socket(&rtp->rtcp_udp, ip, port + 1);
+ if (rc < 0) {
+ LOGP(DRTP, LOGL_ERROR, "Failed to connect RTCP socket: ip=%08x, port=%d\n", ip, port);
+ return rc;
+ }
+
+ LOGP(DRTP, LOGL_INFO, "Sockets connected to: ip=%d port(RTP)=%d port(RTCP)=%d\n", ip, port, port + 1);
+
+ return 0;
+}
+
diff --git a/src/common/support.c b/src/common/support.c
new file mode 100644
index 00000000..1bee3e12
--- /dev/null
+++ b/src/common/support.c
@@ -0,0 +1,83 @@
+/* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/bts/support.h>
+#include <osmocore/protocol/gsm_12_21.h>
+
+struct bts_support bts_support;
+
+void bts_support_init(void)
+{
+ struct bts_support *sup = &bts_support;
+ int i;
+
+ memset(sup, 0, sizeof(*sup));
+
+ /* crypto supprot */
+ sup->a5_1 = 0;
+ sup->a5_2 = 0;
+ sup->a5_3 = 0;
+ sup->a5_4 = 0;
+ sup->a5_5 = 0;
+ sup->a5_6 = 0;
+ sup->a5_7 = 0;
+ /* set supported frequencies */
+ for(i = 1; i <= 124; i++) // P-GSM
+ sup->freq_map[i >> 3] |= (1 << (i & 7));
+ for(i = 512; i <= 885; i++) // DCS
+ sup->freq_map[i >> 3] |= (1 << (i & 7));
+ for(i = 975; i <= 1023; i++) // E-GSM extension
+ sup->freq_map[i >> 3] |= (1 << (i & 7));
+ sup->freq_map[0] |= 1; // channel 0
+ for(i = 955; i <= 974; i++) // R-GSM extension
+ sup->freq_map[i >> 3] |= (1 << (i & 7));
+ /* channel combinations */
+ sup->chan_comb[NM_CHANC_mainBCCH] = 1;
+ sup->chan_comb[NM_CHANC_BCCHComb] = 1;
+ sup->chan_comb[NM_CHANC_SDCCH] = 1;
+ sup->chan_comb[NM_CHANC_TCHFull] = 1;
+ sup->chan_comb[NM_CHANC_TCHHalf] = 1;
+ /* codec */
+ sup->full_v1 = 1;
+ sup->full_v2 = 1;
+ sup->full_v3 = 1;
+ sup->half_v1 = 1;
+ sup->half_v3 = 1;
+}
+
+char *bts_support_comb_name(uint8_t chan_comb)
+{
+ if (chan_comb == NM_CHANC_mainBCCH)
+ return("BCCH");
+ if (chan_comb == NM_CHANC_BCCHComb)
+ return("BCCH+SDCCH/4");
+ if (chan_comb == NM_CHANC_SDCCH)
+ return("SDCCH/8");
+ if (chan_comb == NM_CHANC_TCHFull)
+ return("TCH/F");
+ if (chan_comb == NM_CHANC_TCHHalf)
+ return("TCH/H");
+ return "Unknown";
+}
+
+
diff --git a/src/osmo-bts-bb/main.c b/src/osmo-bts-bb/main.c
new file mode 100644
index 00000000..5dd5a799
--- /dev/null
+++ b/src/osmo-bts-bb/main.c
@@ -0,0 +1,216 @@
+/* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <netinet/in.h>
+#include <osmocore/talloc.h>
+#include <osmocore/signal.h>
+#include <osmocore/timer.h>
+#include <osmocore/select.h>
+#include <osmocore/signal.h>
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/bts/support.h>
+#include <osmocom/bb/bts/abis.h>
+#include <osmocom/bb/bts/rtp.h>
+#include <osmocom/bb/bts/bts.h>
+
+#include <net/if.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <netdb.h>
+#include <string.h>
+#include <time.h>
+
+struct log_target *stderr_target;
+char *debugs = "DL1C:DLAPDM:DABIS:DOML:DRSL:DSUM";
+
+void *l23_ctx = NULL;
+static struct osmocom_bts *bts;
+int debug_set = 0;
+char *software_version = "0.0";
+uint8_t abis_mac[6] = { 0, 1, 2, 3, 4, 5 };
+char *bsc_host = "localhost";
+char *bts_id = "1801/0";
+int quit = 0;
+
+// FIXME this is a hack
+static void get_mac(void)
+{
+ struct if_nameindex *ifn = if_nameindex();
+ struct ifreq ifr;
+ int sock;
+ int ret;
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
+ return;
+
+ memset(&ifr, 0, sizeof(ifr));
+ if (!ifn)
+ return;
+ while (ifn->if_name) {
+ strncpy(ifr.ifr_name, ifn->if_name, sizeof(ifr.ifr_name)-1);
+ ret = ioctl(sock, SIOCGIFHWADDR, &ifr);
+ if (ret == 0 && !!memcmp(ifr.ifr_hwaddr.sa_data,
+ "\0\0\0\0\0\0", 6)) {
+ memcpy(abis_mac, ifr.ifr_hwaddr.sa_data, 6);
+ printf("Using MAC address of %s: "
+ "'%02x:%02x:%02x:%02x:%02x:%02x'\n",
+ ifn->if_name,
+ abis_mac[0], abis_mac[1], abis_mac[2],
+ abis_mac[3], abis_mac[4], abis_mac[5]);
+ break;
+ }
+ ifn++;
+ }
+// if_freenameindex(ifn);
+}
+
+static void print_usage(const char *app)
+{
+ printf("Usage: %s [option]\n", app);
+ printf(" -h --help this text\n");
+ printf(" -d --debug Change debug flags. (-d %s)\n", debugs);
+ printf(" -i --bsc-ip IP address of the BSC\n");
+}
+
+static void handle_options(int argc, char **argv)
+{
+ while (1) {
+ int option_index = 0, c;
+ static struct option long_options[] = {
+ {"help", 0, 0, 'h'},
+ {"debug", 1, 0, 'd'},
+ {"bsc-ip", 1, 0, 'i'},
+ {0, 0, 0, 0},
+ };
+
+ c = getopt_long(argc, argv, "hd:i:",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_usage(argv[0]);
+ exit(0);
+ case 'd':
+ log_parse_category_mask(stderr_target, optarg);
+ debug_set = 1;
+ break;
+ case 'i':
+ bsc_host = strdup(optarg);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void sighandler(int sigset)
+{
+ if (sigset == SIGHUP || sigset == SIGPIPE)
+ return;
+
+ fprintf(stderr, "Signal %d recevied.\n", sigset);
+
+ /* in case there is a lockup during exit */
+ signal(SIGINT, SIG_DFL);
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGPIPE, SIG_DFL);
+
+ quit = 1;
+// dispatch_signal(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL);
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+ struct hostent *hostent;
+ struct in_addr *ina;
+ uint32_t bsc_ip;
+ uint8_t maskv_tx[2], maskv_rx[2];
+
+ printf("((*))\n");
+ printf(" |\n");
+ printf(" / \\ OsmoBTS\n");
+
+ get_mac();
+ bts_support_init();
+
+ srand(time(NULL));
+
+ log_init(&log_info);
+ stderr_target = log_target_create_stderr();
+ log_add_target(stderr_target);
+ log_set_all_filter(stderr_target, 1);
+
+ l23_ctx = talloc_named_const(NULL, 1, "layer2 context");
+
+ handle_options(argc, argv);
+
+ if (!debug_set)
+ log_parse_category_mask(stderr_target, debugs);
+ log_set_log_level(stderr_target, LOGL_INFO);
+
+ hostent = gethostbyname(bsc_host);
+ if (!hostent) {
+ fprintf(stderr, "Failed to resolve BSC hostname '%s'.\n",
+ bsc_host);
+ }
+ ina = (struct in_addr *) hostent->h_addr;
+ bsc_ip = ntohl(ina->s_addr);
+ printf("Using BSC at IP: '%d.%d.%d.%d'\n", bsc_ip >> 24,
+ (bsc_ip >> 16) & 0xff, (bsc_ip >> 8) & 0xff, bsc_ip & 0xff);
+
+ printf("Using BTS ID: '%s'\n", bts_id);
+ bts = create_bts(1, bts_id);
+ if (!bts)
+ exit(-1);
+ maskv_tx[0] = 0x55;
+ maskv_rx[0] = 0x55;
+ ret = create_ms(bts->trx[0], 1, maskv_tx, maskv_rx);
+ if (ret < 0)
+ goto fail;
+ ret = abis_open(&bts->link, bsc_ip);
+ if (ret < 0)
+ goto fail;
+
+ signal(SIGINT, sighandler);
+ signal(SIGHUP, sighandler);
+ signal(SIGTERM, sighandler);
+ signal(SIGPIPE, sighandler);
+
+ while (!quit) {
+ work_bts(bts);
+ bsc_select_main(0);
+ }
+
+fail:
+ destroy_bts(bts);
+
+ return 0;
+}