--- /dev/null
- gsmtap.h write_queue.h rsl.h
+osmocore_HEADERS = signal.h linuxlist.h timer.h talloc.h msgb.h select.h \
+ tlv.h bitvec.h comp128.h statistics.h gsm_utils.h utils.h \
++ gsmtap.h write_queue.h rsl.h gsm48.h
+
+osmocoredir = $(includedir)/osmocore
+
+SUBDIRS = protocol
--- /dev/null
--- /dev/null
++#ifndef _OSMOCORE_GSM48_H
++
++#include <osmocore/tlv.h>
++#include <osmocore/protocol/gsm_04_08.h>
++
++extern const struct tlv_definition gsm48_att_tlvdef;
++extern const char *cc_state_names[32];
++const char *gsm48_cc_msg_names[0x40];
++const char *rr_cause_name(uint8_t cause);
++
++void gsm48_generate_lai(struct gsm48_loc_area_id *lai48, uint16_t mcc,
++ uint16_t mnc, uint16_t lac);
++int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi);
++int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi);
++
++#endif
--- /dev/null
- * (C) 2009 by Harald Welte <laforge@gnumonks.org>
+/* GSM utility functions, e.g. coding and decoding */
+/*
+ * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
++ * (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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.
+ *
+ */
+
+#ifndef GSM_UTILS_H
+#define GSM_UTILS_H
+
+#include <stdint.h>
+
+enum gsm_band {
+ GSM_BAND_850 = 1,
+ GSM_BAND_900 = 2,
+ GSM_BAND_1800 = 4,
+ GSM_BAND_1900 = 8,
+ GSM_BAND_450 = 0x10,
+ GSM_BAND_480 = 0x20,
+ GSM_BAND_750 = 0x40,
+ GSM_BAND_810 = 0x80,
+};
+
++char *gsm_band_name(enum gsm_band band);
++enum gsm_band gsm_band_parse(const char *mhz);
++
+int gsm_7bit_decode(char *decoded, const uint8_t *user_data, uint8_t length);
+int gsm_7bit_encode(uint8_t *result, const char *data);
+
+int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm);
+int ms_pwr_dbm(enum gsm_band band, uint8_t lvl);
+
+/* According to TS 08.05 Chapter 8.1.4 */
+int rxlev2dbm(uint8_t rxlev);
+uint8_t dbm2rxlev(int dbm);
+
+/* According to GSM 04.08 Chapter 10.5.2.29 */
+static inline int rach_max_trans_val2raw(int val) { return (val >> 1) & 3; }
+static inline int rach_max_trans_raw2val(int raw) {
+ const int tbl[4] = { 1, 2, 4, 7 };
+ return tbl[raw & 3];
+}
+
+void generate_backtrace();
+#endif
--- /dev/null
- int rsl_rll_push_l3(struct msgb *msg, uint8_t msg_type,
- uint8_t chan_nr, uint8_t link_id);
+#ifndef _OSMOCORE_RSL_H
+#define _OSMOCORE_RSL_H
+
+#include <stdint.h>
+#include <osmocore/utils.h>
+#include <osmocore/protocol/gsm_08_58.h>
+
+void rsl_init_rll_hdr(struct abis_rsl_rll_hdr *dh, uint8_t msg_type);
+
+extern const struct tlv_definition rsl_att_tlvdef;
+#define rsl_tlv_parse(dec, buf, len) \
+ tlv_parse(dec, &rsl_att_tlvdef, buf, len, 0, 0)
+
+/* encode channel number as per Section 9.3.1 */
+uint8_t rsl_enc_chan_nr(uint8_t type, uint8_t subch, uint8_t timeslot);
+
+const struct value_string rsl_rlm_cause_strs[];
+
+const char *rsl_err_name(uint8_t err);
+
+/* Section 3.3.2.3 TS 05.02. I think this looks like a table */
+int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf);
+
+/* Push a RSL RLL header with L3_INFO IE */
- uint8_t link_id);
++void rsl_rll_push_l3(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr,
++ uint8_t link_id, int transparent);
+
+/* Allocate msgb and fill with simple RSL RLL header */
+struct msgb *rsl_rll_simple(uint8_t msg_type, uint8_t chan_nr,
++ uint8_t link_id, int transparent);
+#endif /* _OSMOCORE_RSL_H */
--- /dev/null
+#ifndef OSMOCORE_UTIL_H
+#define OSMOCORE_UTIL_H
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#include <stdint.h>
+
+struct value_string {
+ unsigned int value;
+ const char *str;
+};
+
+const char *get_value_string(const struct value_string *vs, uint32_t val);
+int get_string_value(const struct value_string *vs, const char *str);
+
++char bcd2char(uint8_t bcd);
++/* only works for numbers in ascci */
++uint8_t char2bcd(char c);
+
+#endif
--- /dev/null
- write_queue.c utils.c rsl.c
+# This is _NOT_ the library release version, it's an API version.
+# Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification
+LIBVERSION=0:0:0
+
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+AM_CFLAGS = -fPIC -Wall
+
+lib_LTLIBRARIES = libosmocore.la
+
+libosmocore_la_SOURCES = msgb.c timer.c talloc.c select.c signal.c \
+ tlv_parser.c bitvec.c comp128.c gsm_utils.c statistics.c \
++ write_queue.c utils.c rsl.c gsm48.c
--- /dev/null
--- /dev/null
++/* GSM Mobile Radio Interface Layer 3 messages
++ * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
++
++/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
++ * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
++ *
++ * 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 <stdint.h>
++#include <stdio.h>
++#include <string.h>
++
++#include <arpa/inet.h>
++
++#include <osmocore/utils.h>
++#include <osmocore/tlv.h>
++#include <osmocore/gsm48.h>
++
++#include <osmocore/protocol/gsm_04_08.h>
++
++const struct tlv_definition gsm48_att_tlvdef = {
++ .def = {
++ [GSM48_IE_MOBILE_ID] = { TLV_TYPE_TLV },
++ [GSM48_IE_NAME_LONG] = { TLV_TYPE_TLV },
++ [GSM48_IE_NAME_SHORT] = { TLV_TYPE_TLV },
++ [GSM48_IE_UTC] = { TLV_TYPE_TV },
++ [GSM48_IE_NET_TIME_TZ] = { TLV_TYPE_FIXED, 7 },
++ [GSM48_IE_LSA_IDENT] = { TLV_TYPE_TLV },
++
++ [GSM48_IE_BEARER_CAP] = { TLV_TYPE_TLV },
++ [GSM48_IE_CAUSE] = { TLV_TYPE_TLV },
++ [GSM48_IE_CC_CAP] = { TLV_TYPE_TLV },
++ [GSM48_IE_ALERT] = { TLV_TYPE_TLV },
++ [GSM48_IE_FACILITY] = { TLV_TYPE_TLV },
++ [GSM48_IE_PROGR_IND] = { TLV_TYPE_TLV },
++ [GSM48_IE_AUX_STATUS] = { TLV_TYPE_TLV },
++ [GSM48_IE_NOTIFY] = { TLV_TYPE_TV },
++ [GSM48_IE_KPD_FACILITY] = { TLV_TYPE_TV },
++ [GSM48_IE_SIGNAL] = { TLV_TYPE_TV },
++ [GSM48_IE_CONN_BCD] = { TLV_TYPE_TLV },
++ [GSM48_IE_CONN_SUB] = { TLV_TYPE_TLV },
++ [GSM48_IE_CALLING_BCD] = { TLV_TYPE_TLV },
++ [GSM48_IE_CALLING_SUB] = { TLV_TYPE_TLV },
++ [GSM48_IE_CALLED_BCD] = { TLV_TYPE_TLV },
++ [GSM48_IE_CALLED_SUB] = { TLV_TYPE_TLV },
++ [GSM48_IE_REDIR_BCD] = { TLV_TYPE_TLV },
++ [GSM48_IE_REDIR_SUB] = { TLV_TYPE_TLV },
++ [GSM48_IE_LOWL_COMPAT] = { TLV_TYPE_TLV },
++ [GSM48_IE_HIGHL_COMPAT] = { TLV_TYPE_TLV },
++ [GSM48_IE_USER_USER] = { TLV_TYPE_TLV },
++ [GSM48_IE_SS_VERS] = { TLV_TYPE_TLV },
++ [GSM48_IE_MORE_DATA] = { TLV_TYPE_T },
++ [GSM48_IE_CLIR_SUPP] = { TLV_TYPE_T },
++ [GSM48_IE_CLIR_INVOC] = { TLV_TYPE_T },
++ [GSM48_IE_REV_C_SETUP] = { TLV_TYPE_T },
++ [GSM48_IE_REPEAT_CIR] = { TLV_TYPE_T },
++ [GSM48_IE_REPEAT_SEQ] = { TLV_TYPE_T },
++ /* FIXME: more elements */
++ },
++};
++
++static const char *rr_cause_names[] = {
++ [GSM48_RR_CAUSE_NORMAL] = "Normal event",
++ [GSM48_RR_CAUSE_ABNORMAL_UNSPEC] = "Abnormal release, unspecified",
++ [GSM48_RR_CAUSE_ABNORMAL_UNACCT] = "Abnormal release, channel unacceptable",
++ [GSM48_RR_CAUSE_ABNORMAL_TIMER] = "Abnormal release, timer expired",
++ [GSM48_RR_CAUSE_ABNORMAL_NOACT] = "Abnormal release, no activity on radio path",
++ [GSM48_RR_CAUSE_PREMPTIVE_REL] = "Preemptive release",
++ [GSM48_RR_CAUSE_HNDOVER_IMP] = "Handover impossible, timing advance out of range",
++ [GSM48_RR_CAUSE_CHAN_MODE_UNACCT] = "Channel mode unacceptable",
++ [GSM48_RR_CAUSE_FREQ_NOT_IMPL] = "Frequency not implemented",
++ [GSM48_RR_CAUSE_CALL_CLEARED] = "Call already cleared",
++ [GSM48_RR_CAUSE_SEMANT_INCORR] = "Semantically incorrect message",
++ [GSM48_RR_CAUSE_INVALID_MAND_INF] = "Invalid mandatory information",
++ [GSM48_RR_CAUSE_MSG_TYPE_N] = "Message type non-existant or not implemented",
++ [GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT] = "Message type not compatible with protocol state",
++ [GSM48_RR_CAUSE_COND_IE_ERROR] = "Conditional IE error",
++ [GSM48_RR_CAUSE_NO_CELL_ALLOC_A] = "No cell allocation available",
++ [GSM48_RR_CAUSE_PROT_ERROR_UNSPC] = "Protocol error unspecified",
++};
++
++const char *cc_state_names[32] = {
++ "NULL",
++ "INITIATED",
++ "illegal state 2",
++ "MO_CALL_PROC",
++ "CALL_DELIVERED",
++ "illegal state 5",
++ "CALL_PRESENT",
++ "CALL_RECEIVED",
++ "CONNECT_REQUEST",
++ "MO_TERM_CALL_CONF",
++ "ACTIVE",
++ "DISCONNECT_REQ",
++ "DISCONNECT_IND",
++ "illegal state 13",
++ "illegal state 14",
++ "illegal state 15",
++ "illegal state 16",
++ "illegal state 17",
++ "illegal state 18",
++ "RELEASE_REQ",
++ "illegal state 20",
++ "illegal state 21",
++ "illegal state 22",
++ "illegal state 23",
++ "illegal state 24",
++ "illegal state 25",
++ "MO_ORIG_MODIFY",
++ "MO_TERM_MODIFY",
++ "CONNECT_IND",
++ "illegal state 29",
++ "illegal state 30",
++ "illegal state 31",
++};
++
++const char *gsm48_cc_msg_names[0x40] = {
++ "unknown 0x00",
++ "ALERTING",
++ "CALL_PROC",
++ "PROGRESS",
++ "ESTAB",
++ "SETUP",
++ "ESTAB_CONF",
++ "CONNECT",
++ "CALL_CONF",
++ "START_CC",
++ "unknown 0x0a",
++ "RECALL",
++ "unknown 0x0c",
++ "unknown 0x0d",
++ "EMERG_SETUP",
++ "CONNECT_ACK",
++ "USER_INFO",
++ "unknown 0x11",
++ "unknown 0x12",
++ "MODIFY_REJECT",
++ "unknown 0x14",
++ "unknown 0x15",
++ "unknown 0x16",
++ "MODIFY",
++ "HOLD",
++ "HOLD_ACK",
++ "HOLD_REJ",
++ "unknown 0x1b",
++ "RETR",
++ "RETR_ACK",
++ "RETR_REJ",
++ "MODIFY_COMPL",
++ "unknown 0x20",
++ "unknown 0x21",
++ "unknown 0x22",
++ "unknown 0x23",
++ "unknown 0x24",
++ "DISCONNECT",
++ "unknown 0x26",
++ "unknown 0x27",
++ "unknown 0x28",
++ "unknown 0x29",
++ "RELEASE_COMPL",
++ "unknown 0x2b",
++ "unknown 0x2c",
++ "RELEASE",
++ "unknown 0x2e",
++ "unknown 0x2f",
++ "unknown 0x30",
++ "STOP_DTMF",
++ "STOP_DTMF_ACK",
++ "unknown 0x33",
++ "STATUS_ENQ",
++ "START_DTMF",
++ "START_DTMF_ACK",
++ "START_DTMF_REJ",
++ "unknown 0x38",
++ "CONG_CTRL",
++ "FACILITY",
++ "unknown 0x3b",
++ "STATUS",
++ "unknown 0x3d",
++ "NOTIFY",
++ "unknown 0x3f",
++};
++
++static char strbuf[64];
++
++const char *rr_cause_name(uint8_t cause)
++{
++ if (cause < ARRAY_SIZE(rr_cause_names) &&
++ rr_cause_names[cause])
++ return rr_cause_names[cause];
++
++ snprintf(strbuf, sizeof(strbuf), "0x%02x", cause);
++ return strbuf;
++}
++
++static void to_bcd(uint8_t *bcd, uint16_t val)
++{
++ bcd[2] = val % 10;
++ val = val / 10;
++ bcd[1] = val % 10;
++ val = val / 10;
++ bcd[0] = val % 10;
++ val = val / 10;
++}
++
++void gsm48_generate_lai(struct gsm48_loc_area_id *lai48, uint16_t mcc,
++ uint16_t mnc, uint16_t lac)
++{
++ uint8_t bcd[3];
++
++ to_bcd(bcd, mcc);
++ lai48->digits[0] = bcd[0] | (bcd[1] << 4);
++ lai48->digits[1] = bcd[2];
++
++ to_bcd(bcd, mnc);
++ /* FIXME: do we need three-digit MNC? See Table 10.5.3 */
++#if 0
++ lai48->digits[1] |= bcd[2] << 4;
++ lai48->digits[2] = bcd[0] | (bcd[1] << 4);
++#else
++ lai48->digits[1] |= 0xf << 4;
++ lai48->digits[2] = bcd[1] | (bcd[2] << 4);
++#endif
++
++ lai48->lac = htons(lac);
++}
++
++int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi)
++{
++ uint32_t *tptr = (uint32_t *) &buf[3];
++
++ buf[0] = GSM48_IE_MOBILE_ID;
++ buf[1] = GSM48_TMSI_LEN;
++ buf[2] = 0xf0 | GSM_MI_TYPE_TMSI;
++ *tptr = htonl(tmsi);
++
++ return 7;
++}
++
++int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi)
++{
++ unsigned int length = strlen(imsi), i, off = 0;
++ uint8_t odd = (length & 0x1) == 1;
++
++ buf[0] = GSM48_IE_MOBILE_ID;
++ buf[2] = char2bcd(imsi[0]) << 4 | GSM_MI_TYPE_IMSI | (odd << 3);
++
++ /* if the length is even we will fill half of the last octet */
++ if (odd)
++ buf[1] = (length + 1) >> 1;
++ else
++ buf[1] = (length + 2) >> 1;
++
++ for (i = 1; i < buf[1]; ++i) {
++ uint8_t lower, upper;
++
++ lower = char2bcd(imsi[++off]);
++ if (!odd && off + 1 == length)
++ upper = 0x0f;
++ else
++ upper = char2bcd(imsi[++off]) & 0x0f;
++
++ buf[2 + i] = (upper << 4) | lower;
++ }
++
++ return 2 + buf[1];
++}
--- /dev/null
+/*
+ * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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 <openbsc/gsm_data.h>
+#include <osmocore/utils.h>
+#include <osmocore/gsm_utils.h>
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
++#include <ctype.h>
+
+#include "../config.h"
+
+/* GSM 03.38 6.2.1 Charachter packing */
+int gsm_7bit_decode(char *text, const uint8_t *user_data, uint8_t length)
+{
+ int i = 0;
+ int l = 0;
+
+ /* FIXME: We need to account for user data headers here */
+ i += l;
+ for (; i < length; i ++)
+ *(text ++) =
+ ((user_data[(i * 7 + 7) >> 3] <<
+ (7 - ((i * 7 + 7) & 7))) |
+ (user_data[(i * 7) >> 3] >>
+ ((i * 7) & 7))) & 0x7f;
+ *text = '\0';
+
+ return i - l;
+}
+
+
+/* GSM 03.38 6.2.1 Charachter packing */
+int gsm_7bit_encode(uint8_t *result, const char *data)
+{
+ int i,j = 0;
+ unsigned char ch1, ch2;
+ int shift = 0;
+
+ for ( i=0; i<strlen(data); i++ ) {
+
+ ch1 = data[i] & 0x7F;
+ ch1 = ch1 >> shift;
+ ch2 = data[(i+1)] & 0x7F;
+ ch2 = ch2 << (7-shift);
+
+ ch1 = ch1 | ch2;
+
+ result[j++] = ch1;
+
+ shift++;
+
+ if ((shift == 7) && (i+1<strlen(data))) {
+ shift = 0;
+ i++;
+ }
+ }
+
+ return i;
+}
+
+/* determine power control level for given dBm value, as indicated
+ * by the tables in chapter 4.1.1 of GSM TS 05.05 */
+int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm)
+{
+ switch (band) {
+ case GSM_BAND_450:
+ case GSM_BAND_480:
+ case GSM_BAND_750:
+ case GSM_BAND_900:
+ case GSM_BAND_810:
+ case GSM_BAND_850:
+ if (dbm >= 39)
+ return 0;
+ else if (dbm < 5)
+ return 19;
+ else {
+ /* we are guaranteed to have (5 <= dbm < 39) */
+ return 2 + ((39 - dbm) / 2);
+ }
+ break;
+ case GSM_BAND_1800:
+ if (dbm >= 36)
+ return 29;
+ else if (dbm >= 34)
+ return 30;
+ else if (dbm >= 32)
+ return 31;
+ else if (dbm == 31)
+ return 0;
+ else {
+ /* we are guaranteed to have (0 <= dbm < 31) */
+ return (30 - dbm) / 2;
+ }
+ break;
+ case GSM_BAND_1900:
+ if (dbm >= 33)
+ return 30;
+ else if (dbm >= 32)
+ return 31;
+ else if (dbm == 31)
+ return 0;
+ else {
+ /* we are guaranteed to have (0 <= dbm < 31) */
+ return (30 - dbm) / 2;
+ }
+ break;
+ }
+ return -EINVAL;
+}
+
+int ms_pwr_dbm(enum gsm_band band, uint8_t lvl)
+{
+ lvl &= 0x1f;
+
+ switch (band) {
+ case GSM_BAND_450:
+ case GSM_BAND_480:
+ case GSM_BAND_750:
+ case GSM_BAND_900:
+ case GSM_BAND_810:
+ case GSM_BAND_850:
+ if (lvl < 2)
+ return 39;
+ else if (lvl < 20)
+ return 39 - ((lvl - 2) * 2) ;
+ else
+ return 5;
+ break;
+ case GSM_BAND_1800:
+ if (lvl < 16)
+ return 30 - (lvl * 2);
+ else if (lvl < 29)
+ return 0;
+ else
+ return 36 - ((lvl - 29) * 2);
+ break;
+ case GSM_BAND_1900:
+ if (lvl < 16)
+ return 30 - (lvl * 2);
+ else if (lvl < 30)
+ return -EINVAL;
+ else
+ return 33 - (lvl - 30);
+ break;
+ }
+ return -EINVAL;
+}
+
+/* According to TS 08.05 Chapter 8.1.4 */
+int rxlev2dbm(uint8_t rxlev)
+{
+ if (rxlev > 63)
+ rxlev = 63;
+
+ return -110 + rxlev;
+}
+
+/* According to TS 08.05 Chapter 8.1.4 */
+uint8_t dbm2rxlev(int dbm)
+{
+ int rxlev = dbm + 110;
+
+ if (rxlev > 63)
+ rxlev = 63;
+ else if (rxlev < 0)
+ rxlev = 0;
+
+ return rxlev;
+}
+
++char *gsm_band_name(enum gsm_band band)
++{
++ switch (band) {
++ case GSM_BAND_450:
++ return "GSM450";
++ case GSM_BAND_480:
++ return "GSM450";
++ case GSM_BAND_750:
++ return "GSM750";
++ case GSM_BAND_810:
++ return "GSM810";
++ case GSM_BAND_850:
++ return "GSM850";
++ case GSM_BAND_900:
++ return "GSM900";
++ case GSM_BAND_1800:
++ return "DCS1800";
++ case GSM_BAND_1900:
++ return "PCS1900";
++ }
++ return "invalid";
++}
++
++enum gsm_band gsm_band_parse(const char* mhz)
++{
++ while (*mhz && !isdigit(*mhz))
++ mhz++;
++
++ if (*mhz == '\0')
++ return -EINVAL;
++
++ switch (atoi(mhz)) {
++ case 450:
++ return GSM_BAND_450;
++ case 480:
++ return GSM_BAND_480;
++ case 750:
++ return GSM_BAND_750;
++ case 810:
++ return GSM_BAND_810;
++ case 850:
++ return GSM_BAND_850;
++ case 900:
++ return GSM_BAND_900;
++ case 1800:
++ return GSM_BAND_1800;
++ case 1900:
++ return GSM_BAND_1900;
++ default:
++ return -EINVAL;
++ }
++}
++
++
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+void generate_backtrace()
+{
+ int i, nptrs;
+ void *buffer[100];
+ char **strings;
+
+ nptrs = backtrace(buffer, ARRAY_SIZE(buffer));
+ printf("backtrace() returned %d addresses\n", nptrs);
+
+ strings = backtrace_symbols(buffer, nptrs);
+ if (!strings)
+ return;
+
+ for (i = 1; i < nptrs; i++)
+ printf("%s\n", strings[i]);
+
+ free(strings);
+}
+#endif
--- /dev/null
- /* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+/* GSM Radio Signalling Link messages on the A-bis interface
+ * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */
+
- subch &= 0x07;
++/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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 <osmocore/tlv.h>
+#include <osmocore/rsl.h>
+
++#define RSL_ALLOC_SIZE 200
++#define RSL_ALLOC_HEADROOM 56
++
+void rsl_init_rll_hdr(struct abis_rsl_rll_hdr *dh, uint8_t msg_type)
+{
+ dh->c.msg_discr = ABIS_RSL_MDISC_RLL;
+ dh->c.msg_type = msg_type;
+ dh->ie_chan = RSL_IE_CHAN_NR;
+ dh->ie_link_id = RSL_IE_LINK_IDENT;
+}
+
+const struct tlv_definition rsl_att_tlvdef = {
+ .def = {
+ [RSL_IE_CHAN_NR] = { TLV_TYPE_TV },
+ [RSL_IE_LINK_IDENT] = { TLV_TYPE_TV },
+ [RSL_IE_ACT_TYPE] = { TLV_TYPE_TV },
+ [RSL_IE_BS_POWER] = { TLV_TYPE_TV },
+ [RSL_IE_CHAN_IDENT] = { TLV_TYPE_TLV },
+ [RSL_IE_CHAN_MODE] = { TLV_TYPE_TLV },
+ [RSL_IE_ENCR_INFO] = { TLV_TYPE_TLV },
+ [RSL_IE_FRAME_NUMBER] = { TLV_TYPE_FIXED, 2 },
+ [RSL_IE_HANDO_REF] = { TLV_TYPE_TV },
+ [RSL_IE_L1_INFO] = { TLV_TYPE_FIXED, 2 },
+ [RSL_IE_L3_INFO] = { TLV_TYPE_TL16V },
+ [RSL_IE_MS_IDENTITY] = { TLV_TYPE_TLV },
+ [RSL_IE_MS_POWER] = { TLV_TYPE_TV },
+ [RSL_IE_PAGING_GROUP] = { TLV_TYPE_TV },
+ [RSL_IE_PAGING_LOAD] = { TLV_TYPE_FIXED, 2 },
+ [RSL_IE_PYHS_CONTEXT] = { TLV_TYPE_TLV },
+ [RSL_IE_ACCESS_DELAY] = { TLV_TYPE_TV },
+ [RSL_IE_RACH_LOAD] = { TLV_TYPE_TLV },
+ [RSL_IE_REQ_REFERENCE] = { TLV_TYPE_FIXED, 3 },
+ [RSL_IE_RELEASE_MODE] = { TLV_TYPE_TV },
+ [RSL_IE_RESOURCE_INFO] = { TLV_TYPE_TLV },
+ [RSL_IE_RLM_CAUSE] = { TLV_TYPE_TLV },
+ [RSL_IE_STARTNG_TIME] = { TLV_TYPE_FIXED, 2 },
+ [RSL_IE_TIMING_ADVANCE] = { TLV_TYPE_TV },
+ [RSL_IE_UPLINK_MEAS] = { TLV_TYPE_TLV },
+ [RSL_IE_CAUSE] = { TLV_TYPE_TLV },
+ [RSL_IE_MEAS_RES_NR] = { TLV_TYPE_TV },
+ [RSL_IE_MSG_ID] = { TLV_TYPE_TV },
+ [RSL_IE_SYSINFO_TYPE] = { TLV_TYPE_TV },
+ [RSL_IE_MS_POWER_PARAM] = { TLV_TYPE_TLV },
+ [RSL_IE_BS_POWER_PARAM] = { TLV_TYPE_TLV },
+ [RSL_IE_PREPROC_PARAM] = { TLV_TYPE_TLV },
+ [RSL_IE_PREPROC_MEAS] = { TLV_TYPE_TLV },
+ [RSL_IE_IMM_ASS_INFO] = { TLV_TYPE_TLV },
+ [RSL_IE_SMSCB_INFO] = { TLV_TYPE_FIXED, 23 },
+ [RSL_IE_MS_TIMING_OFFSET] = { TLV_TYPE_TV },
+ [RSL_IE_ERR_MSG] = { TLV_TYPE_TLV },
+ [RSL_IE_FULL_BCCH_INFO] = { TLV_TYPE_TLV },
+ [RSL_IE_CHAN_NEEDED] = { TLV_TYPE_TV },
+ [RSL_IE_CB_CMD_TYPE] = { TLV_TYPE_TV },
+ [RSL_IE_SMSCB_MSG] = { TLV_TYPE_TLV },
+ [RSL_IE_FULL_IMM_ASS_INFO] = { TLV_TYPE_TLV },
+ [RSL_IE_SACCH_INFO] = { TLV_TYPE_TLV },
+ [RSL_IE_CBCH_LOAD_INFO] = { TLV_TYPE_TV },
+ [RSL_IE_SMSCB_CHAN_INDICATOR] = { TLV_TYPE_TV },
+ [RSL_IE_GROUP_CALL_REF] = { TLV_TYPE_TLV },
+ [RSL_IE_CHAN_DESC] = { TLV_TYPE_TLV },
+ [RSL_IE_NCH_DRX_INFO] = { TLV_TYPE_TLV },
+ [RSL_IE_CMD_INDICATOR] = { TLV_TYPE_TLV },
+ [RSL_IE_EMLPP_PRIO] = { TLV_TYPE_TV },
+ [RSL_IE_UIC] = { TLV_TYPE_TLV },
+ [RSL_IE_MAIN_CHAN_REF] = { TLV_TYPE_TV },
+ [RSL_IE_MR_CONFIG] = { TLV_TYPE_TLV },
+ [RSL_IE_MR_CONTROL] = { TLV_TYPE_TV },
+ [RSL_IE_SUP_CODEC_TYPES] = { TLV_TYPE_TLV },
+ [RSL_IE_CODEC_CONFIG] = { TLV_TYPE_TLV },
+ [RSL_IE_RTD] = { TLV_TYPE_TV },
+ [RSL_IE_TFO_STATUS] = { TLV_TYPE_TV },
+ [RSL_IE_LLP_APDU] = { TLV_TYPE_TLV },
+ [RSL_IE_SIEMENS_MRPCI] = { TLV_TYPE_TV },
+ [RSL_IE_IPAC_PROXY_UDP] = { TLV_TYPE_FIXED, 2 },
+ [RSL_IE_IPAC_BSCMPL_TOUT] = { TLV_TYPE_TV },
+ [RSL_IE_IPAC_REMOTE_IP] = { TLV_TYPE_FIXED, 4 },
+ [RSL_IE_IPAC_REMOTE_PORT] = { TLV_TYPE_FIXED, 2 },
+ [RSL_IE_IPAC_RTP_PAYLOAD] = { TLV_TYPE_TV },
+ [RSL_IE_IPAC_LOCAL_PORT] = { TLV_TYPE_FIXED, 2 },
+ [RSL_IE_IPAC_SPEECH_MODE] = { TLV_TYPE_TV },
+ [RSL_IE_IPAC_LOCAL_IP] = { TLV_TYPE_FIXED, 4 },
+ [RSL_IE_IPAC_CONN_ID] = { TLV_TYPE_FIXED, 2 },
+ [RSL_IE_IPAC_RTP_CSD_FMT] = { TLV_TYPE_TV },
+ [RSL_IE_IPAC_RTP_JIT_BUF] = { TLV_TYPE_FIXED, 2 },
+ [RSL_IE_IPAC_RTP_COMPR] = { TLV_TYPE_TV },
+ [RSL_IE_IPAC_RTP_PAYLOAD2] = { TLV_TYPE_TV },
+ [RSL_IE_IPAC_RTP_MPLEX] = { TLV_TYPE_FIXED, 8 },
+ [RSL_IE_IPAC_RTP_MPLEX_ID] = { TLV_TYPE_TV },
+ },
+};
+
+/* encode channel number as per Section 9.3.1 */
+uint8_t rsl_enc_chan_nr(uint8_t type, uint8_t subch, uint8_t timeslot)
+{
+ uint8_t ret;
+
+ ret = (timeslot & 0x07) | type;
+
+ switch (type) {
+ case RSL_CHAN_Lm_ACCHs:
+ subch &= 0x01;
+ break;
+ case RSL_CHAN_SDCCH4_ACCH:
- int rsl_rll_push_l3(struct msgb *msg, uint8_t msg_type,
- uint8_t chan_nr, uint8_t link_id)
++ subch &= 0x03;
+ break;
+ case RSL_CHAN_SDCCH8_ACCH:
+ subch &= 0x07;
+ break;
+ default:
+ /* no subchannels allowed */
+ subch = 0x00;
+ break;
+ }
+ ret |= (subch << 3);
+
+ return ret;
+}
+
+/* FIXME: convert to value_string */
+static const char *rsl_err_vals[0xff] = {
+ [RSL_ERR_RADIO_IF_FAIL] = "Radio Interface Failure",
+ [RSL_ERR_RADIO_LINK_FAIL] = "Radio Link Failure",
+ [RSL_ERR_HANDOVER_ACC_FAIL] = "Handover Access Failure",
+ [RSL_ERR_TALKER_ACC_FAIL] = "Talker Access Failure",
+ [RSL_ERR_OM_INTERVENTION] = "O&M Intervention",
+ [RSL_ERR_NORMAL_UNSPEC] = "Normal event, unspecified",
+ [RSL_ERR_T_MSRFPCI_EXP] = "Siemens: T_MSRFPCI Expired",
+ [RSL_ERR_EQUIPMENT_FAIL] = "Equipment Failure",
+ [RSL_ERR_RR_UNAVAIL] = "Radio Resource not available",
+ [RSL_ERR_TERR_CH_FAIL] = "Terrestrial Channel Failure",
+ [RSL_ERR_CCCH_OVERLOAD] = "CCCH Overload",
+ [RSL_ERR_ACCH_OVERLOAD] = "ACCH Overload",
+ [RSL_ERR_PROCESSOR_OVERLOAD] = "Processor Overload",
+ [RSL_ERR_RES_UNAVAIL] = "Resource not available, unspecified",
+ [RSL_ERR_TRANSC_UNAVAIL] = "Transcoding not available",
+ [RSL_ERR_SERV_OPT_UNAVAIL] = "Service or Option not available",
+ [RSL_ERR_ENCR_UNIMPL] = "Encryption algorithm not implemented",
+ [RSL_ERR_SERV_OPT_UNIMPL] = "Service or Option not implemented",
+ [RSL_ERR_RCH_ALR_ACTV_ALLOC] = "Radio channel already activated",
+ [RSL_ERR_INVALID_MESSAGE] = "Invalid Message, unspecified",
+ [RSL_ERR_MSG_DISCR] = "Message Discriminator Error",
+ [RSL_ERR_MSG_TYPE] = "Message Type Error",
+ [RSL_ERR_MSG_SEQ] = "Message Sequence Error",
+ [RSL_ERR_IE_ERROR] = "General IE error",
+ [RSL_ERR_MAND_IE_ERROR] = "Mandatory IE error",
+ [RSL_ERR_OPT_IE_ERROR] = "Optional IE error",
+ [RSL_ERR_IE_NONEXIST] = "IE non-existent",
+ [RSL_ERR_IE_LENGTH] = "IE length error",
+ [RSL_ERR_IE_CONTENT] = "IE content error",
+ [RSL_ERR_PROTO] = "Protocol error, unspecified",
+ [RSL_ERR_INTERWORKING] = "Interworking error, unspecified",
+};
+
+const struct value_string rsl_rlm_cause_strs[] = {
+ { RLL_CAUSE_T200_EXPIRED, "Timer T200 expired (N200+1) times" },
+ { RLL_CAUSE_REEST_REQ, "Re-establishment request" },
+ { RLL_CAUSE_UNSOL_UA_RESP, "Unsolicited UA response" },
+ { RLL_CAUSE_UNSOL_DM_RESP, "Unsolicited DM response" },
+ { RLL_CAUSE_UNSOL_DM_RESP_MF, "Unsolicited DM response, multiple frame" },
+ { RLL_CAUSE_UNSOL_SPRV_RESP, "Unsolicited supervisory response" },
+ { RLL_CAUSE_SEQ_ERR, "Sequence Error" },
+ { RLL_CAUSE_UFRM_INC_PARAM, "U-Frame with incorrect parameters" },
+ { RLL_CAUSE_SFRM_INC_PARAM, "S-Frame with incorrect parameters" },
+ { RLL_CAUSE_IFRM_INC_MBITS, "I-Frame with incorrect use of M bit" },
+ { RLL_CAUSE_IFRM_INC_LEN, "I-Frame with incorrect length" },
+ { RLL_CAUSE_FRM_UNIMPL, "Fraeme not implemented" },
+ { RLL_CAUSE_SABM_MF, "SABM command, multiple frame established state" },
+ { RLL_CAUSE_SABM_INFO_NOTALL, "SABM frame with information not allowed in this state" },
+ { 0, NULL },
+};
+
+const char *rsl_err_name(uint8_t err)
+{
+ if (rsl_err_vals[err])
+ return rsl_err_vals[err];
+ else
+ return "unknown";
+}
+
+/* Section 3.3.2.3 TS 05.02. I think this looks like a table */
+int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf)
+{
+ switch (ccch_conf) {
+ case RSL_BCCH_CCCH_CONF_1_NC:
+ return 1;
+ case RSL_BCCH_CCCH_CONF_1_C:
+ return 1;
+ case RSL_BCCH_CCCH_CONF_2_NC:
+ return 2;
+ case RSL_BCCH_CCCH_CONF_3_NC:
+ return 3;
+ case RSL_BCCH_CCCH_CONF_4_NC:
+ return 4;
+ default:
+ return -1;
+ }
+}
+
+/* Section 3.3.2.3 TS 05.02 */
+int rsl_ccch_conf_to_bs_ccch_sdcch_comb(int ccch_conf)
+{
+ switch (ccch_conf) {
+ case RSL_BCCH_CCCH_CONF_1_NC:
+ return 0;
+ case RSL_BCCH_CCCH_CONF_1_C:
+ return 1;
+ case RSL_BCCH_CCCH_CONF_2_NC:
+ return 0;
+ case RSL_BCCH_CCCH_CONF_3_NC:
+ return 0;
+ case RSL_BCCH_CCCH_CONF_4_NC:
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+/* Push a RSL RLL header with L3_INFO IE */
- rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP;
++void rsl_rll_push_l3(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr,
++ uint8_t link_id, int transparent)
+{
+ uint8_t l3_len = msg->tail - (uint8_t *)msgb_l3(msg);
+ struct abis_rsl_rll_hdr *rh;
+
+ /* construct a RSLms RLL message (DATA INDICATION, UNIT DATA
+ * INDICATION) and send it off via RSLms */
+
+ /* Push the L3 IE tag and lengh */
+ msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len);
+
+ /* Then push the RSL header */
+ rh = (struct abis_rsl_rll_hdr *) msgb_push(msg, sizeof(*rh));
+ rsl_init_rll_hdr(rh, msg_type);
-
- return 0;
++ if (transparent)
++ rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP;
+ rh->chan_nr = chan_nr;
+ rh->link_id = link_id;
+
+ /* set the l2 header pointer */
+ msg->l2h = (uint8_t *)rh;
- uint8_t link_id)
+}
+
+struct msgb *rsl_rll_simple(uint8_t msg_type, uint8_t chan_nr,
- struct msgb *msg = msgb_alloc(sizeof(*rh), "rsl_rll_simple");
++ uint8_t link_id, int transparent)
+{
+ struct abis_rsl_rll_hdr *rh;
- rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP;
++ struct msgb *msg;
++
++ msg = msgb_alloc_headroom(RSL_ALLOC_SIZE+RSL_ALLOC_HEADROOM,
++ RSL_ALLOC_HEADROOM, "rsl_rll_simple");
+
+ if (!msg)
+ return NULL;
+
+ /* put the RSL header */
+ rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh));
+ rsl_init_rll_hdr(rh, msg_type);
++ if (transparent)
++ rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP;
+ rh->chan_nr = chan_nr;
+ rh->link_id = link_id;
+
+ /* set the l2 header pointer */
+ msg->l2h = (uint8_t *)rh;
+
+ return msg;
+}
--- /dev/null
+
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocore/utils.h>
+
+const char *get_value_string(const struct value_string *vs, uint32_t val)
+{
+ int i;
+
+ for (i = 0;; i++) {
+ if (vs[i].value == 0 && vs[i].str == NULL)
+ break;
+ if (vs[i].value == val)
+ return vs[i].str;
+ }
+ return "unknown";
+}
+
+int get_string_value(const struct value_string *vs, const char *str)
+{
+ int i;
+
+ for (i = 0;; i++) {
+ if (vs[i].value == 0 && vs[i].str == NULL)
+ break;
+ if (!strcasecmp(vs[i].str, str))
+ return vs[i].value;
+ }
+ return -EINVAL;
+}
++
++char bcd2char(uint8_t bcd)
++{
++ if (bcd < 0xa)
++ return '0' + bcd;
++ else
++ return 'A' + (bcd - 0xa);
++}
++
++/* only works for numbers in ascci */
++uint8_t char2bcd(char c)
++{
++ return c - 0x30;
++}