diff options
author | Harald Welte <laforge@gnumonks.org> | 2011-03-04 14:05:20 +0100 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2011-03-04 14:05:20 +0100 |
commit | 4b45ae1636990a15934f77f37033cf8ed5804b57 (patch) | |
tree | 892d687a5c689fdb7813a3f94dd9a0926d80b892 |
Import all C and Header files from jolly/bts branch of osmocom-bb.git
The BTS code shall reside in a separate git repository, thus I'm
importing the C and H files here.
-rw-r--r-- | include/osmo-bts/abis.h | 68 | ||||
-rw-r--r-- | include/osmo-bts/bts.h | 133 | ||||
-rw-r--r-- | include/osmo-bts/oml.h | 9 | ||||
-rw-r--r-- | include/osmo-bts/rsl.h | 12 | ||||
-rw-r--r-- | include/osmo-bts/rtp.h | 47 | ||||
-rw-r--r-- | include/osmo-bts/support.h | 30 | ||||
-rw-r--r-- | src/common/abis.c | 488 | ||||
-rw-r--r-- | src/common/bts.c | 376 | ||||
-rw-r--r-- | src/common/l1ctl.c | 305 | ||||
-rw-r--r-- | src/common/oml.c | 549 | ||||
-rw-r--r-- | src/common/rsl.c | 787 | ||||
-rw-r--r-- | src/common/rtp.c | 517 | ||||
-rw-r--r-- | src/common/support.c | 83 | ||||
-rw-r--r-- | src/osmo-bts-bb/main.c | 216 |
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; +} |