--- /dev/null
- 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
--- /dev/null
- 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
--- /dev/null
--- /dev/null
++#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
--- /dev/null
- 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
--- /dev/null
--- /dev/null
++#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
--- /dev/null
- 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 */
--- /dev/null
+/* 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
--- /dev/null
- 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
--- /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 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;
++}
++
--- /dev/null
- 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));
+}
--- /dev/null
- 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;
+}