Merge commit '61e2bfc5f44267a7a3b0b25ff3ab922fca2a199c'
authorHarald Welte <laforge@gnumonks.org>
Thu, 4 Mar 2010 10:59:12 +0000 (11:59 +0100)
committerHarald Welte <laforge@gnumonks.org>
Thu, 4 Mar 2010 10:59:12 +0000 (11:59 +0100)
1  2 
src/shared/libosmocore/include/osmocore/Makefile.am
src/shared/libosmocore/include/osmocore/gsm48.h
src/shared/libosmocore/include/osmocore/gsm_utils.h
src/shared/libosmocore/include/osmocore/rsl.h
src/shared/libosmocore/include/osmocore/utils.h
src/shared/libosmocore/src/Makefile.am
src/shared/libosmocore/src/gsm48.c
src/shared/libosmocore/src/gsm_utils.c
src/shared/libosmocore/src/rsl.c
src/shared/libosmocore/src/utils.c

index 8754295,0000000..b45d023
mode 100644,000000..100644
--- /dev/null
@@@ -1,7 -1,0 +1,7 @@@
-                  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
index 0000000,0000000..95963d5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++#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
index 57521ac,0000000..b611050
mode 100644,000000..100644
--- /dev/null
@@@ -1,59 -1,0 +1,62 @@@
-  * (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
index 250c6ea,0000000..0da0520
mode 100644,000000..100644
--- /dev/null
@@@ -1,31 -1,0 +1,31 @@@
- 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 */
index cf3b460,0000000..51c6f03
mode 100644,000000..100644
--- /dev/null
@@@ -1,17 -1,0 +1,20 @@@
 +#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
index 65415ee,0000000..b6826ea
mode 100644,000000..100644
--- /dev/null
@@@ -1,12 -1,0 +1,12 @@@
-                        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
index 0000000,0000000..ff989ea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,283 @@@
++/* 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];
++}
index b0a66a6,0000000..174b2d4
mode 100644,000000..100644
--- /dev/null
@@@ -1,215 -1,0 +1,270 @@@
 +/*
 + * (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
index 62cd562,0000000..58afc9d
mode 100644,000000..100644
--- /dev/null
@@@ -1,287 -1,0 +1,293 @@@
- /* (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;
 +}
index 0d878c7,0000000..2a73d39
mode 100644,000000..100644
--- /dev/null
@@@ -1,32 -1,0 +1,46 @@@
 +
 +#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;
++}