Merge commit 'cbc8062ef8ba10690aa64b433d0efdccb46f496a'
authorHarald Welte <laforge@gnumonks.org>
Mon, 22 Mar 2010 00:30:11 +0000 (08:30 +0800)
committerHarald Welte <laforge@gnumonks.org>
Mon, 22 Mar 2010 00:30:11 +0000 (08:30 +0800)
1  2 
src/shared/libosmocore/include/osmocore/Makefile.am
src/shared/libosmocore/include/osmocore/gsm48.h
src/shared/libosmocore/include/osmocore/gsm48_ie.h
src/shared/libosmocore/include/osmocore/gsm_utils.h
src/shared/libosmocore/include/osmocore/mncc.h
src/shared/libosmocore/include/osmocore/rsl.h
src/shared/libosmocore/include/osmocore/write_queue.h
src/shared/libosmocore/src/Makefile.am
src/shared/libosmocore/src/gsm48_ie.c
src/shared/libosmocore/src/gsm_utils.c
src/shared/libosmocore/src/write_queue.c

index adcae53,0000000..fb4f089
mode 100644,000000..100644
--- /dev/null
@@@ -1,11 -1,0 +1,12 @@@
-                  gsmtap.h write_queue.h rsl.h gsm48.h rxlev_stat.h
 +osmocore_HEADERS = signal.h linuxlist.h timer.h select.h msgb.h \
 +                 tlv.h bitvec.h comp128.h statistics.h gsm_utils.h utils.h \
++                 gsmtap.h write_queue.h rsl.h gsm48.h rxlev_stat.h mncc.h \
++                 gsm48_ie.h
 +
 +if ENABLE_TALLOC
 +osmocore_HEADERS += talloc.h
 +endif
 +
 +osmocoredir = $(includedir)/osmocore
 +
 +SUBDIRS = protocol
index 95963d5,0000000..787cdd0
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,17 @@@
- const char *gsm48_cc_msg_names[0x40];
 +#ifndef _OSMOCORE_GSM48_H
 +
 +#include <osmocore/tlv.h>
 +#include <osmocore/protocol/gsm_04_08.h>
++#include <osmocore/gsm48_ie.h>
 +
 +extern const struct tlv_definition gsm48_att_tlvdef;
 +extern const char *cc_state_names[32];
++extern 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 0000000,0000000..200619a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,107 @@@
++#ifndef _OSMOCORE_GSM48_IE_H
++#define _OSMOCORE_GSM48_IE_H
++
++#include <stdint.h>
++#include <string.h>
++#include <errno.h>
++
++#include <osmocore/msgb.h>
++#include <osmocore/tlv.h>
++#include <osmocore/mncc.h>
++#include <osmocore/protocol/gsm_04_08.h>
++
++/* decode a 'called/calling/connect party BCD number' as in 10.5.4.7 */
++int gsm48_decode_bcd_number(char *output, int output_len,
++                          const uint8_t *bcd_lv, int h_len);
++
++/* convert a ASCII phone number to 'called/calling/connect party BCD number' */
++int gsm48_encode_bcd_number(uint8_t *bcd_lv, uint8_t max_len,
++                          int h_len, const char *input);
++/* decode 'bearer capability' */
++int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
++                           const uint8_t *lv);
++/* encode 'bearer capability' */
++int gsm48_encode_bearer_cap(struct msgb *msg, int lv_only,
++                           const struct gsm_mncc_bearer_cap *bcap);
++/* decode 'call control cap' */
++int gsm48_decode_cccap(struct gsm_mncc_cccap *ccap, const uint8_t *lv);
++/* encode 'call control cap' */
++int gsm48_encode_cccap(struct msgb *msg,
++                      const struct gsm_mncc_cccap *ccap);
++/* decode 'called party BCD number' */
++int gsm48_decode_called(struct gsm_mncc_number *called,
++                       const uint8_t *lv);
++/* encode 'called party BCD number' */
++int gsm48_encode_called(struct msgb *msg,
++                       const struct gsm_mncc_number *called);
++/* decode callerid of various IEs */
++int gsm48_decode_callerid(struct gsm_mncc_number *callerid,
++                       const uint8_t *lv);
++/* encode callerid of various IEs */
++int gsm48_encode_callerid(struct msgb *msg, int ie, int max_len,
++                         const struct gsm_mncc_number *callerid);
++/* decode 'cause' */
++int gsm48_decode_cause(struct gsm_mncc_cause *cause,
++                      const uint8_t *lv);
++/* encode 'cause' */
++int gsm48_encode_cause(struct msgb *msg, int lv_only,
++                      const struct gsm_mncc_cause *cause);
++/* decode 'calling number' */
++int gsm48_decode_calling(struct gsm_mncc_number *calling,
++                       const uint8_t *lv);
++/* encode 'calling number' */
++int gsm48_encode_calling(struct msgb *msg, 
++                        const struct gsm_mncc_number *calling);
++/* decode 'connected number' */
++int gsm48_decode_connected(struct gsm_mncc_number *connected,
++                       const uint8_t *lv);
++/* encode 'connected number' */
++int gsm48_encode_connected(struct msgb *msg,
++                          const struct gsm_mncc_number *connected);
++/* decode 'redirecting number' */
++int gsm48_decode_redirecting(struct gsm_mncc_number *redirecting,
++                       const uint8_t *lv);
++/* encode 'redirecting number' */
++int gsm48_encode_redirecting(struct msgb *msg,
++                            const struct gsm_mncc_number *redirecting);
++/* decode 'facility' */
++int gsm48_decode_facility(struct gsm_mncc_facility *facility,
++                         const uint8_t *lv);
++/* encode 'facility' */
++int gsm48_encode_facility(struct msgb *msg, int lv_only,
++                         const struct gsm_mncc_facility *facility);
++/* decode 'notify' */
++int gsm48_decode_notify(int *notify, const uint8_t *v);
++/* encode 'notify' */
++int gsm48_encode_notify(struct msgb *msg, int notify);
++/* decode 'signal' */
++int gsm48_decode_signal(int *signal, const uint8_t *v);
++/* encode 'signal' */
++int gsm48_encode_signal(struct msgb *msg, int signal);
++/* decode 'keypad' */
++int gsm48_decode_keypad(int *keypad, const uint8_t *lv);
++/* encode 'keypad' */
++int gsm48_encode_keypad(struct msgb *msg, int keypad);
++/* decode 'progress' */
++int gsm48_decode_progress(struct gsm_mncc_progress *progress,
++                         const uint8_t *lv);
++/* encode 'progress' */
++int gsm48_encode_progress(struct msgb *msg, int lv_only,
++                         const struct gsm_mncc_progress *p);
++/* decode 'user-user' */
++int gsm48_decode_useruser(struct gsm_mncc_useruser *uu,
++                         const uint8_t *lv);
++/* encode 'useruser' */
++int gsm48_encode_useruser(struct msgb *msg, int lv_only,
++                         const struct gsm_mncc_useruser *uu);
++/* decode 'ss version' */
++int gsm48_decode_ssversion(struct gsm_mncc_ssversion *ssv,
++                          const uint8_t *lv);
++/* encode 'ss version' */
++int gsm48_encode_ssversion(struct msgb *msg,
++                         const struct gsm_mncc_ssversion *ssv);
++/* decode 'more data' does not require a function, because it has no value */
++/* encode 'more data' */
++int gsm48_encode_more(struct msgb *msg);
++
++#endif
index 2536045,0000000..c87e967
mode 100644,000000..100644
--- /dev/null
@@@ -1,84 -1,0 +1,84 @@@
- char *gsm_band_name(enum gsm_band band);
 +/* 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>
 +
 +struct gsm_time {
 +      uint32_t        fn;     /* FN count */
 +      uint16_t        t1;     /* FN div (26*51) */
 +      uint8_t         t2;     /* FN modulo 26 */
 +      uint8_t         t3;     /* FN modulo 51 */
 +      uint8_t         tc;
 +};
 +
 +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,
 +};
 +
++const 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];
 +}
 +
 +#define       ARFCN_PCS       0x8000
 +#define       ARFCN_UPLINK    0x4000
 +
 +enum gsm_band gsm_arfcn2band(uint16_t arfcn);
 +
 +/* Convert an ARFCN to the frequency in MHz * 10 */
 +uint16_t gsm_arfcn2freq10(uint16_t arfcn, int uplink);
 +
 +/* Convert from frame number to GSM time */
 +void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn);
 +
 +/* Convert from GSM time to frame number */
 +uint32_t gsm_gsmtime2fn(struct gsm_time *time);
 +
 +void generate_backtrace();
 +#endif
index 0000000,0000000..a094bb9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,71 @@@
++#ifndef _OSMOCORE_MNCC_H
++#define _OSMOCORE_MNCC_H
++
++#define GSM_MAX_FACILITY       128
++#define GSM_MAX_SSVERSION      128
++#define GSM_MAX_USERUSER       128
++
++/* Expanded fields from GSM TS 04.08, Table 10.5.102 */
++struct gsm_mncc_bearer_cap {
++      int             transfer;       /* Information Transfer Capability */
++      int             mode;           /* Transfer Mode */
++      int             coding;         /* Coding Standard */
++      int             radio;          /* Radio Channel Requirement */
++      int             speech_ctm;     /* CTM text telephony indication */
++      int             speech_ver[8];  /* Speech version indication */
++};
++
++struct gsm_mncc_number {
++      int             type;
++      int             plan;
++      int             present;
++      int             screen;
++      char            number[33];
++};
++
++struct gsm_mncc_cause {
++      int             location;
++      int             coding;
++      int             rec;
++      int             rec_val;
++      int             value;
++      int             diag_len;
++      char            diag[32];
++};
++
++struct gsm_mncc_useruser {
++      int             proto;
++      char            info[GSM_MAX_USERUSER + 1]; /* + termination char */
++};
++
++struct gsm_mncc_progress {
++      int             coding;
++      int             location;
++      int             descr;
++};
++
++struct gsm_mncc_facility {
++      int             len;
++      char            info[GSM_MAX_FACILITY];
++};
++
++struct gsm_mncc_ssversion {
++      int             len;
++      char            info[GSM_MAX_SSVERSION];
++};
++
++struct gsm_mncc_cccap {
++      int             dtmf;
++      int             pcp;
++};
++
++enum {
++      GSM_MNCC_BCAP_SPEECH    = 0,
++      GSM_MNCC_BCAP_UNR_DIG   = 1,
++      GSM_MNCC_BCAP_AUDIO     = 2,
++      GSM_MNCC_BCAP_FAX_G3    = 3,
++      GSM_MNCC_BCAP_OTHER_ITC = 5,
++      GSM_MNCC_BCAP_RESERVED  = 7,
++};
++
++#endif
index 4c5f75b,0000000..c108081
mode 100644,000000..100644
--- /dev/null
@@@ -1,33 -1,0 +1,33 @@@
- const struct value_string rsl_rlm_cause_strs[];
 +#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);
 +/* decode channel number as per Section 9.3.1 */
 +int rsl_dec_chan_nr(uint8_t chan_nr, uint8_t *type, uint8_t *subch, uint8_t *timeslot);
 +
++extern 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 */
 +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 af3d44b,0000000..c84000c
mode 100644,000000..100644
--- /dev/null
@@@ -1,43 -1,0 +1,44 @@@
 +/* Generic write queue implementation */
 +/*
 + * (C) 2010 by Holger Hans Peter Freyther
 + * (C) 2010 by On-Waves
 + *
 + * All Rights Reserved
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU 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 write_queue_h
 +#define write_queue_h
 +
 +#include "select.h"
 +#include "msgb.h"
 +
 +struct write_queue {
 +      struct bsc_fd bfd;
 +      unsigned int max_length;
 +      unsigned int current_length;
 +
 +      struct llist_head msg_queue;
 +
 +      int (*read_cb)(struct bsc_fd *fd);
 +      int (*write_cb)(struct bsc_fd *fd, struct msgb *msg);
 +};
 +
 +void write_queue_init(struct write_queue *queue, int max_length);
 +int write_queue_enqueue(struct write_queue *queue, struct msgb *data);
++int write_queue_bfd_cb(struct bsc_fd *fd, unsigned int what);
 +
 +#endif
index 5122e73,0000000..f0effa2
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,16 @@@
- libosmocore_la_SOURCES = timer.c select.c signal.c msgb.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
 +
-                        write_queue.c utils.c rsl.c gsm48.c rxlev_stat.c
++libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c rxlev_stat.c \
 +                       tlv_parser.c bitvec.c comp128.c gsm_utils.c statistics.c \
++                       write_queue.c utils.c rsl.c gsm48.c gsm48_ie.c
 +
 +if ENABLE_TALLOC
 +libosmocore_la_SOURCES += talloc.c
 +endif
index 0000000,0000000..4ca5fb8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,659 @@@
++/* 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 by Harald Welte <laforge@gnumonks.org>
++ * (C) 2008-2010 by Andreas Eversberg
++ *
++ * 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 <string.h>
++#include <errno.h>
++
++#include <osmocore/utils.h>
++#include <osmocore/msgb.h>
++#include <osmocore/tlv.h>
++#include <osmocore/mncc.h>
++#include <osmocore/protocol/gsm_04_08.h>
++
++static const char bcd_num_digits[] = {
++      '0', '1', '2', '3', '4', '5', '6', '7',
++      '8', '9', '*', '#', 'a', 'b', 'c', '\0'
++};
++
++/* decode a 'called/calling/connect party BCD number' as in 10.5.4.7 */
++int gsm48_decode_bcd_number(char *output, int output_len,
++                          const uint8_t *bcd_lv, int h_len)
++{
++      uint8_t in_len = bcd_lv[0];
++      int i;
++
++      for (i = 1 + h_len; i <= in_len; i++) {
++              /* lower nibble */
++              output_len--;
++              if (output_len <= 1)
++                      break;
++              *output++ = bcd_num_digits[bcd_lv[i] & 0xf];
++
++              /* higher nibble */
++              output_len--;
++              if (output_len <= 1)
++                      break;
++              *output++ = bcd_num_digits[bcd_lv[i] >> 4];
++      }
++      if (output_len >= 1)
++              *output++ = '\0';
++
++      return 0;
++}
++
++/* convert a single ASCII character to call-control BCD */
++static int asc_to_bcd(const char asc)
++{
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(bcd_num_digits); i++) {
++              if (bcd_num_digits[i] == asc)
++                      return i;
++      }
++      return -EINVAL;
++}
++
++/* convert a ASCII phone number to 'called/calling/connect party BCD number' */
++int gsm48_encode_bcd_number(uint8_t *bcd_lv, uint8_t max_len,
++                    int h_len, const char *input)
++{
++      int in_len = strlen(input);
++      int i;
++      uint8_t *bcd_cur = bcd_lv + 1 + h_len;
++
++      /* two digits per byte, plus type byte */
++      bcd_lv[0] = in_len/2 + h_len;
++      if (in_len % 2)
++              bcd_lv[0]++;
++
++      if (bcd_lv[0] > max_len)
++              return -EIO;
++
++      for (i = 0; i < in_len; i++) {
++              int rc = asc_to_bcd(input[i]);
++              if (rc < 0)
++                      return rc;
++              if (i % 2 == 0)
++                      *bcd_cur = rc;
++              else
++                      *bcd_cur++ |= (rc << 4);
++      }
++      /* append padding nibble in case of odd length */
++      if (i % 2)
++              *bcd_cur++ |= 0xf0;
++
++      /* return how many bytes we used */
++      return (bcd_cur - bcd_lv);
++}
++
++/* decode 'bearer capability' */
++int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
++                           const uint8_t *lv)
++{
++      uint8_t in_len = lv[0];
++      int i, s;
++
++      if (in_len < 1)
++              return -EINVAL;
++
++      bcap->speech_ver[0] = -1; /* end of list, of maximum 7 values */
++
++      /* octet 3 */
++      bcap->transfer = lv[1] & 0x07;
++      bcap->mode = (lv[1] & 0x08) >> 3;
++      bcap->coding = (lv[1] & 0x10) >> 4;
++      bcap->radio = (lv[1] & 0x60) >> 5;
++
++      if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) {
++              i = 1;
++              s = 0;
++              while(!(lv[i] & 0x80)) {
++                      i++; /* octet 3a etc */
++                      if (in_len < i)
++                              return 0;
++                      bcap->speech_ver[s++] = lv[i] & 0x0f;
++                      bcap->speech_ver[s] = -1; /* end of list */
++                      if (i == 2) /* octet 3a */
++                              bcap->speech_ctm = (lv[i] & 0x20) >> 5;
++                      if (s == 7) /* maximum speech versions + end of list */
++                              return 0;
++              }
++      } else {
++              i = 1;
++              while (!(lv[i] & 0x80)) {
++                      i++; /* octet 3a etc */
++                      if (in_len < i)
++                              return 0;
++                      /* ignore them */
++              }
++              /* FIXME: implement OCTET 4+ parsing */
++      }
++
++      return 0;
++}
++
++/* encode 'bearer capability' */
++int gsm48_encode_bearer_cap(struct msgb *msg, int lv_only,
++                           const struct gsm_mncc_bearer_cap *bcap)
++{
++      uint8_t lv[32 + 1];
++      int i = 1, s;
++
++      lv[1] = bcap->transfer;
++      lv[1] |= bcap->mode << 3;
++      lv[1] |= bcap->coding << 4;
++      lv[1] |= bcap->radio << 5;
++
++      if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) {
++              for (s = 0; bcap->speech_ver[s] >= 0; s++) {
++                      i++; /* octet 3a etc */
++                      lv[i] = bcap->speech_ver[s];
++                      if (i == 2) /* octet 3a */
++                              lv[i] |= bcap->speech_ctm << 5;
++              }
++              lv[i] |= 0x80; /* last IE of octet 3 etc */
++      } else {
++              /* FIXME: implement OCTET 4+ encoding */
++      }
++
++      lv[0] = i;
++      if (lv_only)
++              msgb_lv_put(msg, lv[0], lv+1);
++      else
++              msgb_tlv_put(msg, GSM48_IE_BEARER_CAP, lv[0], lv+1);
++
++      return 0;
++}
++
++/* decode 'call control cap' */
++int gsm48_decode_cccap(struct gsm_mncc_cccap *ccap, const uint8_t *lv)
++{
++      uint8_t in_len = lv[0];
++
++      if (in_len < 1)
++              return -EINVAL;
++
++      /* octet 3 */
++      ccap->dtmf = lv[1] & 0x01;
++      ccap->pcp = (lv[1] & 0x02) >> 1;
++
++      return 0;
++}
++
++/* encode 'call control cap' */
++int gsm48_encode_cccap(struct msgb *msg,
++                      const struct gsm_mncc_cccap *ccap)
++{
++      uint8_t lv[2];
++
++      lv[0] = 1;
++      lv[1] = 0;
++      if (ccap->dtmf)
++              lv [1] |= 0x01;
++      if (ccap->pcp)
++              lv [1] |= 0x02;
++
++      msgb_tlv_put(msg, GSM48_IE_CC_CAP, lv[0], lv+1);
++
++      return 0;
++}
++
++/* decode 'called party BCD number' */
++int gsm48_decode_called(struct gsm_mncc_number *called,
++                       const uint8_t *lv)
++{
++      uint8_t in_len = lv[0];
++
++      if (in_len < 1)
++              return -EINVAL;
++
++      /* octet 3 */
++      called->plan = lv[1] & 0x0f;
++      called->type = (lv[1] & 0x70) >> 4;
++
++      /* octet 4..N */
++      gsm48_decode_bcd_number(called->number, sizeof(called->number), lv, 1);
++
++      return 0;
++}
++
++/* encode 'called party BCD number' */
++int gsm48_encode_called(struct msgb *msg,
++                       const struct gsm_mncc_number *called)
++{
++      uint8_t lv[18];
++      int ret;
++
++      /* octet 3 */
++      lv[1] = called->plan;
++      lv[1] |= called->type << 4;
++
++      /* octet 4..N, octet 2 */
++      ret = gsm48_encode_bcd_number(lv, sizeof(lv), 1, called->number);
++      if (ret < 0)
++              return ret;
++
++      msgb_tlv_put(msg, GSM48_IE_CALLED_BCD, lv[0], lv+1);
++
++      return 0;
++}
++
++/* decode callerid of various IEs */
++int gsm48_decode_callerid(struct gsm_mncc_number *callerid,
++                       const uint8_t *lv)
++{
++      uint8_t in_len = lv[0];
++      int i = 1;
++
++      if (in_len < 1)
++              return -EINVAL;
++
++      /* octet 3 */
++      callerid->plan = lv[1] & 0x0f;
++      callerid->type = (lv[1] & 0x70) >> 4;
++
++      /* octet 3a */
++      if (!(lv[1] & 0x80)) {
++              callerid->screen = lv[2] & 0x03;
++              callerid->present = (lv[2] & 0x60) >> 5;
++              i = 2;
++      }
++
++      /* octet 4..N */
++      gsm48_decode_bcd_number(callerid->number, sizeof(callerid->number), lv, i);
++
++      return 0;
++}
++
++/* encode callerid of various IEs */
++int gsm48_encode_callerid(struct msgb *msg, int ie, int max_len,
++                         const struct gsm_mncc_number *callerid)
++{
++      uint8_t lv[max_len - 1];
++      int h_len = 1;
++      int ret;
++
++      /* octet 3 */
++      lv[1] = callerid->plan;
++      lv[1] |= callerid->type << 4;
++
++      if (callerid->present || callerid->screen) {
++              /* octet 3a */
++              lv[2] = callerid->screen;
++              lv[2] |= callerid->present << 5;
++              lv[2] |= 0x80;
++              h_len++;
++      } else
++              lv[1] |= 0x80;
++
++      /* octet 4..N, octet 2 */
++      ret = gsm48_encode_bcd_number(lv, sizeof(lv), h_len, callerid->number);
++      if (ret < 0)
++              return ret;
++
++      msgb_tlv_put(msg, ie, lv[0], lv+1);
++
++      return 0;
++}
++
++/* decode 'cause' */
++int gsm48_decode_cause(struct gsm_mncc_cause *cause,
++                      const uint8_t *lv)
++{
++      uint8_t in_len = lv[0];
++      int i;
++
++      if (in_len < 2)
++              return -EINVAL;
++
++      cause->diag_len = 0;
++
++      /* octet 3 */
++      cause->location = lv[1] & 0x0f;
++      cause->coding = (lv[1] & 0x60) >> 5;
++
++      i = 1;
++      if (!(lv[i] & 0x80)) {
++              i++; /* octet 3a */
++              if (in_len < i+1)
++                      return 0;
++              cause->rec = 1;
++              cause->rec_val = lv[i] & 0x7f;
++      }
++      i++;
++
++      /* octet 4 */
++      cause->value = lv[i] & 0x7f;
++      i++;
++
++      if (in_len < i) /* no diag */
++              return 0;
++
++      if (in_len - (i-1) > 32) /* maximum 32 octets */
++              return 0;
++
++      /* octet 5-N */
++      memcpy(cause->diag, lv + i, in_len - (i-1));
++      cause->diag_len = in_len - (i-1);
++
++      return 0;
++}
++
++/* encode 'cause' */
++int gsm48_encode_cause(struct msgb *msg, int lv_only,
++                      const struct gsm_mncc_cause *cause)
++{
++      uint8_t lv[32+4];
++      int i;
++
++      if (cause->diag_len > 32)
++              return -EINVAL;
++
++      /* octet 3 */
++      lv[1] = cause->location;
++      lv[1] |= cause->coding << 5;
++
++      i = 1;
++      if (cause->rec) {
++              i++; /* octet 3a */
++              lv[i] = cause->rec_val;
++      }
++      lv[i] |= 0x80; /* end of octet 3 */
++
++      /* octet 4 */
++      i++;
++      lv[i] = 0x80 | cause->value;
++
++      /* octet 5-N */
++      if (cause->diag_len) {
++              memcpy(lv + i, cause->diag, cause->diag_len);
++              i += cause->diag_len;
++      }
++
++      lv[0] = i;
++      if (lv_only)
++              msgb_lv_put(msg, lv[0], lv+1);
++      else
++              msgb_tlv_put(msg, GSM48_IE_CAUSE, lv[0], lv+1);
++
++      return 0;
++}
++
++/* decode 'calling number' */
++int gsm48_decode_calling(struct gsm_mncc_number *calling,
++                       const uint8_t *lv)
++{
++      return gsm48_decode_callerid(calling, lv);
++}
++
++/* encode 'calling number' */
++int gsm48_encode_calling(struct msgb *msg, 
++                        const struct gsm_mncc_number *calling)
++{
++      return gsm48_encode_callerid(msg, GSM48_IE_CALLING_BCD, 14, calling);
++}
++
++/* decode 'connected number' */
++int gsm48_decode_connected(struct gsm_mncc_number *connected,
++                       const uint8_t *lv)
++{
++      return gsm48_decode_callerid(connected, lv);
++}
++
++/* encode 'connected number' */
++int gsm48_encode_connected(struct msgb *msg,
++                          const struct gsm_mncc_number *connected)
++{
++      return gsm48_encode_callerid(msg, GSM48_IE_CONN_BCD, 14, connected);
++}
++
++/* decode 'redirecting number' */
++int gsm48_decode_redirecting(struct gsm_mncc_number *redirecting,
++                       const uint8_t *lv)
++{
++      return gsm48_decode_callerid(redirecting, lv);
++}
++
++/* encode 'redirecting number' */
++int gsm48_encode_redirecting(struct msgb *msg,
++                            const struct gsm_mncc_number *redirecting)
++{
++      return gsm48_encode_callerid(msg, GSM48_IE_REDIR_BCD, 19, redirecting);
++}
++
++/* decode 'facility' */
++int gsm48_decode_facility(struct gsm_mncc_facility *facility,
++                         const uint8_t *lv)
++{
++      uint8_t in_len = lv[0];
++
++      if (in_len < 1)
++              return -EINVAL;
++
++      if (in_len > sizeof(facility->info))
++              return -EINVAL;
++
++      memcpy(facility->info, lv+1, in_len);
++      facility->len = in_len;
++
++      return 0;
++}
++
++/* encode 'facility' */
++int gsm48_encode_facility(struct msgb *msg, int lv_only,
++                         const struct gsm_mncc_facility *facility)
++{
++      uint8_t lv[GSM_MAX_FACILITY + 1];
++
++      if (facility->len < 1 || facility->len > GSM_MAX_FACILITY)
++              return -EINVAL;
++
++      memcpy(lv+1, facility->info, facility->len);
++      lv[0] = facility->len;
++      if (lv_only)
++              msgb_lv_put(msg, lv[0], lv+1);
++      else
++              msgb_tlv_put(msg, GSM48_IE_FACILITY, lv[0], lv+1);
++
++      return 0;
++}
++
++/* decode 'notify' */
++int gsm48_decode_notify(int *notify, const uint8_t *v)
++{
++      *notify = v[0] & 0x7f;
++
++      return 0;
++}
++
++/* encode 'notify' */
++int gsm48_encode_notify(struct msgb *msg, int notify)
++{
++      msgb_v_put(msg, notify | 0x80);
++
++      return 0;
++}
++
++/* decode 'signal' */
++int gsm48_decode_signal(int *signal, const uint8_t *v)
++{
++      *signal = v[0];
++
++      return 0;
++}
++
++/* encode 'signal' */
++int gsm48_encode_signal(struct msgb *msg, int signal)
++{
++      msgb_tv_put(msg, GSM48_IE_SIGNAL, signal);
++
++      return 0;
++}
++
++/* decode 'keypad' */
++int gsm48_decode_keypad(int *keypad, const uint8_t *lv)
++{
++      uint8_t in_len = lv[0];
++
++      if (in_len < 1)
++              return -EINVAL;
++
++      *keypad = lv[1] & 0x7f;
++
++      return 0;
++}
++
++/* encode 'keypad' */
++int gsm48_encode_keypad(struct msgb *msg, int keypad)
++{
++      msgb_tv_put(msg, GSM48_IE_KPD_FACILITY, keypad);
++
++      return 0;
++}
++
++/* decode 'progress' */
++int gsm48_decode_progress(struct gsm_mncc_progress *progress,
++                         const uint8_t *lv)
++{
++      uint8_t in_len = lv[0];
++
++      if (in_len < 2)
++              return -EINVAL;
++
++      progress->coding = (lv[1] & 0x60) >> 5;
++      progress->location = lv[1] & 0x0f;
++      progress->descr = lv[2] & 0x7f;
++
++      return 0;
++}
++
++/* encode 'progress' */
++int gsm48_encode_progress(struct msgb *msg, int lv_only,
++                         const struct gsm_mncc_progress *p)
++{
++      uint8_t lv[3];
++
++      lv[0] = 2;
++      lv[1] = 0x80 | ((p->coding & 0x3) << 5) | (p->location & 0xf);
++      lv[2] = 0x80 | (p->descr & 0x7f);
++      if (lv_only)
++              msgb_lv_put(msg, lv[0], lv+1);
++      else
++              msgb_tlv_put(msg, GSM48_IE_PROGR_IND, lv[0], lv+1);
++
++      return 0;
++}
++
++/* decode 'user-user' */
++int gsm48_decode_useruser(struct gsm_mncc_useruser *uu,
++                         const uint8_t *lv)
++{
++      uint8_t in_len = lv[0];
++      char *info = uu->info;
++      int info_len = sizeof(uu->info);
++      int i;
++
++      if (in_len < 1)
++              return -EINVAL;
++
++      uu->proto = lv[1];
++
++      for (i = 2; i <= in_len; i++) {
++              info_len--;
++              if (info_len <= 1)
++                      break;
++              *info++ = lv[i];
++      }
++      if (info_len >= 1)
++              *info++ = '\0';
++
++      return 0;
++}
++
++/* encode 'useruser' */
++int gsm48_encode_useruser(struct msgb *msg, int lv_only,
++                         const struct gsm_mncc_useruser *uu)
++{
++      uint8_t lv[GSM_MAX_USERUSER + 2];
++
++      if (strlen(uu->info) > GSM_MAX_USERUSER)
++              return -EINVAL;
++
++      lv[0] = 1 + strlen(uu->info);
++      lv[1] = uu->proto;
++      memcpy(lv + 2, uu->info, strlen(uu->info));
++      if (lv_only)
++              msgb_lv_put(msg, lv[0], lv+1);
++      else
++              msgb_tlv_put(msg, GSM48_IE_USER_USER, lv[0], lv+1);
++
++      return 0;
++}
++
++/* decode 'ss version' */
++int gsm48_decode_ssversion(struct gsm_mncc_ssversion *ssv,
++                          const uint8_t *lv)
++{
++      uint8_t in_len = lv[0];
++
++      if (in_len < 1 || in_len < sizeof(ssv->info))
++              return -EINVAL;
++
++      memcpy(ssv->info, lv + 1, in_len);
++      ssv->len = in_len;
++
++      return 0;
++}
++
++/* encode 'ss version' */
++int gsm48_encode_ssversion(struct msgb *msg,
++                         const struct gsm_mncc_ssversion *ssv)
++{
++      uint8_t lv[GSM_MAX_SSVERSION + 1];
++
++      if (ssv->len > GSM_MAX_SSVERSION)
++              return -EINVAL;
++
++      lv[0] = ssv->len;
++      memcpy(lv + 1, ssv->info, ssv->len);
++      msgb_tlv_put(msg, GSM48_IE_SS_VERS, lv[0], lv+1);
++
++      return 0;
++}
++
++/* decode 'more data' does not require a function, because it has no value */
++
++/* encode 'more data' */
++int gsm48_encode_more(struct msgb *msg)
++{
++      uint8_t *ie;
++
++      ie = msgb_put(msg, 1);
++      ie[0] = GSM48_IE_MORE_DATA;
++
++      return 0;
++}
++
index 97497c8,0000000..593dd5c
mode 100644,000000..100644
--- /dev/null
@@@ -1,361 -1,0 +1,361 @@@
- char *gsm_band_name(enum gsm_band band)
 +/*
 + * (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.
 + *
 + */
 +
 +//#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;
 +}
 +
++const 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 (strtol(mhz, NULL, 10)) {
 +      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
 +
 +enum gsm_band gsm_arfcn2band(uint16_t arfcn)
 +{
 +      if (arfcn & ARFCN_PCS)
 +              return GSM_BAND_1900;
 +      else if (arfcn <= 124)
 +              return GSM_BAND_900;
 +      else if (arfcn >= 955 && arfcn <= 1023)
 +              return GSM_BAND_900;
 +      else if (arfcn >= 128 && arfcn <= 251)
 +              return GSM_BAND_850;
 +      else if (arfcn >= 512 && arfcn <= 885)
 +              return GSM_BAND_1800;
 +      else if (arfcn >= 259 && arfcn <= 293)
 +              return GSM_BAND_450;
 +      else if (arfcn >= 306 && arfcn <= 340)
 +              return GSM_BAND_480;
 +      else if (arfcn >= 350 && arfcn <= 425)
 +              return GSM_BAND_810;
 +      else if (arfcn >= 438 && arfcn <= 511)
 +              return GSM_BAND_750;
 +      else
 +              return GSM_BAND_1800;
 +}
 +
 +/* Convert an ARFCN to the frequency in MHz * 10 */
 +uint16_t gsm_arfcn2freq10(uint16_t arfcn, int uplink)
 +{
 +      uint16_t freq10_ul;
 +      uint16_t freq10_dl;
 +
 +      if (arfcn & ARFCN_PCS) {
 +              /* DCS 1900 */
 +              arfcn &= ~ARFCN_PCS;
 +              freq10_ul = 18502 + 2 * (arfcn-512);
 +              freq10_dl = freq10_ul + 800;
 +      } else if (arfcn <= 124) {
 +              /* Primary GSM + ARFCN 0 of E-GSM */
 +              freq10_ul = 8900 + 2 * arfcn;
 +              freq10_dl = freq10_ul + 450;
 +      } else if (arfcn >= 955 && arfcn <= 1023) {
 +              /* E-GSM and R-GSM */
 +              freq10_ul = 8900 + 2 * (arfcn - 1024);
 +              freq10_dl = freq10_ul + 450;
 +      } else if (arfcn >= 128 && arfcn <= 251) {
 +              /* GSM 850 */
 +              freq10_ul = 8242 + 2 * (arfcn - 128);
 +              freq10_dl = freq10_ul + 450;
 +      } else if (arfcn >= 512 && arfcn <= 885) {
 +              /* DCS 1800 */
 +              freq10_ul = 17102 + 2 * (arfcn - 512);
 +              freq10_dl = freq10_ul + 950;
 +      } else if (arfcn >= 259 && arfcn <= 293) {
 +              /* GSM 450 */
 +              freq10_ul = 4506 + 2 * (arfcn - 259);
 +              freq10_dl = freq10_ul + 100;
 +      } else if (arfcn >= 306 && arfcn <= 340) {
 +              /* GSM 480 */
 +              freq10_ul = 4790 + 2 * (arfcn - 306);
 +              freq10_dl = freq10_ul + 100;
 +      } else if (arfcn >= 350 && arfcn <= 425) {
 +              /* GSM 810 */
 +              freq10_ul = 8060 + 2 * (arfcn - 350);
 +              freq10_dl = freq10_ul + 450;
 +      } else if (arfcn >= 438 && arfcn <= 511) {
 +              /* GSM 750 */
 +              freq10_ul = 7472 + 2 * (arfcn - 438);
 +              freq10_dl = freq10_ul + 300;
 +      } else
 +              return 0xffff;
 +
 +      if (uplink)
 +              return freq10_ul;
 +      else
 +              return freq10_dl;
 +}
 +
 +void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn)
 +{
 +      time->fn = fn;
 +      time->t1 = time->fn / (26*51);
 +      time->t2 = time->fn % 26;
 +      time->t3 = time->fn % 51;
 +      time->tc = (time->fn / 51) % 8;
 +}
 +
 +uint32_t gsm_gsmtime2fn(struct gsm_time *time)
 +{
 +      /* TS 05.02 Chapter 4.3.3 TDMA frame number */
 +      return (51 * ((time->t3 - time->t2 + 26) % 26) + time->t3 + (26 * 51 * time->t1));
 +}
index 597fbe7,0000000..7d908b4
mode 100644,000000..100644
--- /dev/null
@@@ -1,74 -1,0 +1,74 @@@
- static int queue_cb(struct bsc_fd *fd, unsigned int what)
 +/* Generic write queue implementation */
 +/*
 + * (C) 2010 by Holger Hans Peter Freyther
 + * (C) 2010 by On-Waves
 + *
 + * All Rights Reserved
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU 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/write_queue.h>
 +
-       queue->bfd.cb = queue_cb;
++int write_queue_bfd_cb(struct bsc_fd *fd, unsigned int what)
 +{
 +      struct write_queue *queue;
 +
 +      queue = container_of(fd, struct write_queue, bfd);
 +
 +      if (what & BSC_FD_READ)
 +              queue->read_cb(fd);
 +
 +      if (what & BSC_FD_WRITE) {
 +              struct msgb *msg;
 +
 +              fd->when &= ~BSC_FD_WRITE;
 +              msg = msgb_dequeue(&queue->msg_queue);
 +              if (!msg)
 +                      return -1;
 +
 +              --queue->current_length;
 +              queue->write_cb(fd, msg);
 +              msgb_free(msg);
 +
 +              if (!llist_empty(&queue->msg_queue))
 +                      fd->when |= BSC_FD_WRITE;
 +      }
 +
 +      return 0;
 +}
 +
 +void write_queue_init(struct write_queue *queue, int max_length)
 +{
 +      queue->max_length = max_length;
 +      queue->current_length = 0;
 +      queue->read_cb = NULL;
 +      queue->write_cb = NULL;
++      queue->bfd.cb = write_queue_bfd_cb;
 +      INIT_LLIST_HEAD(&queue->msg_queue);
 +}
 +
 +int write_queue_enqueue(struct write_queue *queue, struct msgb *data)
 +{
 +//    if (queue->current_length + 1 >= queue->max_length)
 +//            LOGP(DMSC, LOGL_ERROR, "The queue is full. Dropping not yet implemented.\n");
 +
 +      ++queue->current_length;
 +      msgb_enqueue(&queue->msg_queue, data);
 +      queue->bfd.when |= BSC_FD_WRITE;
 +
 +      return 0;
 +}