--- /dev/null
++libosmocore (0.3.0) natty; urgency=low
++
++ * New upstream version of libosmocore
++
++ -- Harald Welte <laforge@gnumonks.org> Tue, 10 May 2011 17:28:24 +0200
++
+libosmocore (0.1.27) natty; urgency=low
+
+ * New upstream version of libosmocore.
+
+ -- Holger Hans Peter Freyther <holger@freyther.de> Thu, 13 Jan 2011 18:07:36 +0800
+
+libosmocore (0.1.17-1) unstable; urgency=low
+
+ * Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP>
+
+ -- Harald Welte <laforge@gnumonks.org> Tue, 24 Aug 2010 10:55:04 +0200
--- /dev/null
- usr/include/osmocore
+usr/lib
+usr/include
+usr/include/osmocom
++usr/include/osmocom/codec
++usr/include/osmocom/core
++usr/include/osmocom/crypt
++usr/include/osmocom/gsm
+usr/include/osmocom/vty
--- /dev/null
- usr/include/osmocore
+usr/lib
+usr/include
+usr/include/osmocom
++usr/include/osmocom/codec
++usr/include/osmocom/core
++usr/include/osmocom/crypt
++usr/include/osmocom/gsm
+usr/include/osmocom/vty
--- /dev/null
- backtrace.h
+osmocore_HEADERS = signal.h linuxlist.h timer.h select.h msgb.h bits.h \
+ bitvec.h statistics.h utils.h \
+ gsmtap.h write_queue.h \
+ logging.h rate_ctr.h gsmtap_util.h \
+ plugin.h crc16.h panic.h process.h msgfile.h \
++ backtrace.h conv.h application.h
+
+if ENABLE_TALLOC
+osmocore_HEADERS += talloc.h
+endif
+
+osmocoredir = $(includedir)/osmocom/core
--- /dev/null
--- /dev/null
++#ifndef OSMO_APPLICATION_H
++#define OSMO_APPLICATION_H
++
++/**
++ * Routines for helping with the application setup.
++ */
++
++struct log_info;
++struct log_target;
++
++extern struct log_target *osmo_stderr_target;
++
++void osmo_init_ignore_signals(void);
++int osmo_init_logging(const struct log_info *);
++
++#endif
--- /dev/null
- void generate_backtrace();
+#ifndef _OSMO_BACKTRACE_H_
+#define _OSMO_BACKTRACE_H_
+
++void osmo_generate_backtrace();
+
+#endif
--- /dev/null
--- /dev/null
++/*
++ * conv.h
++ *
++ * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
++ *
++ * 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 __OSMO_CONV_H__
++#define __OSMO_CONV_H__
++
++#include <stdint.h>
++
++#include <osmocom/core/bits.h>
++
++struct osmo_conv_code {
++ int N;
++ int K;
++ int len;
++
++ const uint8_t (*next_output)[2];
++ const uint8_t (*next_state)[2];
++
++ const uint8_t *next_term_output;
++ const uint8_t *next_term_state;
++
++ const int *puncture;
++};
++
++
++/* Encoding */
++
++ /* Low level API */
++struct osmo_conv_encoder {
++ const struct osmo_conv_code *code;
++ int i_idx; /* Next input bit index */
++ int p_idx; /* Current puncture index */
++ uint8_t state; /* Current state */
++};
++
++void osmo_conv_encode_init(struct osmo_conv_encoder *encoder,
++ const struct osmo_conv_code *code);
++int osmo_conv_encode_raw(struct osmo_conv_encoder *encoder,
++ const ubit_t *input, ubit_t *output, int n);
++int osmo_conv_encode_finish(struct osmo_conv_encoder *encoder, ubit_t *output);
++
++ /* All-in-one */
++int osmo_conv_encode(const struct osmo_conv_code *code,
++ const ubit_t *input, ubit_t *output);
++
++
++/* Decoding */
++
++ /* Low level API */
++struct osmo_conv_decoder {
++ const struct osmo_conv_code *code;
++
++ int n_states;
++
++ int len; /* Max o_idx (excl. termination) */
++
++ int o_idx; /* output index */
++ int p_idx; /* puncture index */
++
++ unsigned int *ae; /* accumulater error */
++ unsigned int *ae_next; /* next accumulated error (tmp in scan) */
++ uint8_t *state_history; /* state history [len][n_states] */
++};
++
++void osmo_conv_decode_init(struct osmo_conv_decoder *decoder,
++ const struct osmo_conv_code *code, int len);
++void osmo_conv_decode_reset(struct osmo_conv_decoder *decoder);
++void osmo_conv_decode_deinit(struct osmo_conv_decoder *decoder);
++
++int osmo_conv_decode_scan(struct osmo_conv_decoder *decoder,
++ const sbit_t *input, int n);
++int osmo_conv_decode_finish(struct osmo_conv_decoder *decoder,
++ const sbit_t *input);
++int osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder,
++ ubit_t *output, int has_finish);
++
++ /* All-in-one */
++int osmo_conv_decode(const struct osmo_conv_code *code,
++ const sbit_t *input, ubit_t *output);
++
++
++#endif /* __OSMO_CONV_H__ */
--- /dev/null
- extern uint16_t const crc16_table[256];
+/*
+ * This was copied from the linux kernel and adjusted for our types.
+ */
+/*
+ * crc16.h - CRC-16 routine
+ *
+ * Implements the standard CRC-16:
+ * Width 16
+ * Poly 0x8005 (x^16 + x^15 + x^2 + 1)
+ * Init 0
+ *
+ * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#ifndef __CRC16_H
+#define __CRC16_H
+
+#include <stdint.h>
+
+#include <sys/types.h>
+
- extern uint16_t crc16(uint16_t crc, const uint8_t *buffer, size_t len);
++extern uint16_t const osmo_crc16_table[256];
+
- static inline uint16_t crc16_byte(uint16_t crc, const uint8_t data)
++extern uint16_t osmo_crc16(uint16_t crc, const uint8_t *buffer, size_t len);
+
- return (crc >> 8) ^ crc16_table[(crc ^ data) & 0xff];
++static inline uint16_t osmo_crc16_byte(uint16_t crc, const uint8_t data)
+{
++ return (crc >> 8) ^ osmo_crc16_table[(crc ^ data) & 0xff];
+}
+
+#endif /* __CRC16_H */
--- /dev/null
+#ifndef _GSMTAP_UTIL_H
+#define _GSMTAP_UTIL_H
+
+#include <stdint.h>
+
+/* convert RSL channel number to GSMTAP channel type */
+uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t rsl_link_id);
+
+/* receive a message from L1/L2 and put it in GSMTAP */
+struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type,
+ uint8_t ss, uint32_t fn, int8_t signal_dbm,
+ uint8_t snr, const uint8_t *data, unsigned int len);
+
+/* receive a message from L1/L2 and put it in GSMTAP */
+int gsmtap_sendmsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss,
+ uint32_t fn, int8_t signal_dbm, uint8_t snr,
+ const uint8_t *data, unsigned int len);
+
+int gsmtap_init(uint32_t dst_ip);
+
++/* Create a local 'gsmtap sink' avoiding the UDP packets being rejected
++ * with ICMP reject messages */
++int gsmtap_sink_init(uint32_t bind_ip);
++
+#endif /* _GSMTAP_UTIL_H */
--- /dev/null
- static_assert(size > headroom, headroom_bigger);
+#ifndef _MSGB_H
+#define _MSGB_H
+
+/* (C) 2008 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 <stdint.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/utils.h>
+
+#define MSGB_DEBUG
+
+struct msgb {
+ struct llist_head list;
+
+ /* Part of which TRX logical channel we were received / transmitted */
+ /* FIXME: move them into the control buffer */
+ struct gsm_bts_trx *trx;
+ struct gsm_lchan *lchan;
+
+ /* the Layer1 header (if any) */
+ unsigned char *l1h;
+ /* the A-bis layer 2 header: OML, RSL(RLL), NS */
+ unsigned char *l2h;
+ /* the layer 3 header. For OML: FOM; RSL: 04.08; GPRS: BSSGP */
+ unsigned char *l3h;
+ /* the layer 4 header */
+ unsigned char *l4h;
+
+ /* the 'control buffer', large enough to contain 5 pointers */
+ unsigned long cb[5];
+
+ uint16_t data_len;
+ uint16_t len;
+
+ unsigned char *head;
+ unsigned char *tail;
+ unsigned char *data;
+ unsigned char _data[0];
+};
+
+extern struct msgb *msgb_alloc(uint16_t size, const char *name);
+extern void msgb_free(struct msgb *m);
+extern void msgb_enqueue(struct llist_head *queue, struct msgb *msg);
+extern struct msgb *msgb_dequeue(struct llist_head *queue);
+extern void msgb_reset(struct msgb *m);
+
+#ifdef MSGB_DEBUG
+#include <osmocom/core/panic.h>
+#define MSGB_ABORT(msg, fmt, args ...) do { \
+ osmo_panic("msgb(%p): " fmt, msg, ## args); \
+ } while(0)
+#else
+#define MSGB_ABORT(msg, fmt, args ...)
+#endif
+
+#define msgb_l1(m) ((void *)(m->l1h))
+#define msgb_l2(m) ((void *)(m->l2h))
+#define msgb_l3(m) ((void *)(m->l3h))
+#define msgb_sms(m) ((void *)(m->l4h))
+
+static inline unsigned int msgb_l1len(const struct msgb *msgb)
+{
+ return msgb->tail - (uint8_t *)msgb_l1(msgb);
+}
+
+static inline unsigned int msgb_l2len(const struct msgb *msgb)
+{
+ return msgb->tail - (uint8_t *)msgb_l2(msgb);
+}
+
+static inline unsigned int msgb_l3len(const struct msgb *msgb)
+{
+ return msgb->tail - (uint8_t *)msgb_l3(msgb);
+}
+
+static inline unsigned int msgb_headlen(const struct msgb *msgb)
+{
+ return msgb->len - msgb->data_len;
+}
+
+static inline int msgb_tailroom(const struct msgb *msgb)
+{
+ return (msgb->head + msgb->data_len) - msgb->tail;
+}
+
+static inline int msgb_headroom(const struct msgb *msgb)
+{
+ return (msgb->data - msgb->head);
+}
+
+static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len)
+{
+ unsigned char *tmp = msgb->tail;
+ if (msgb_tailroom(msgb) < (int) len)
+ MSGB_ABORT(msgb, "Not enough tailroom msgb_push (%u < %u)\n",
+ msgb_tailroom(msgb), len);
+ msgb->tail += len;
+ msgb->len += len;
+ return tmp;
+}
+static inline void msgb_put_u8(struct msgb *msgb, uint8_t word)
+{
+ uint8_t *space = msgb_put(msgb, 1);
+ space[0] = word & 0xFF;
+}
+static inline void msgb_put_u16(struct msgb *msgb, uint16_t word)
+{
+ uint8_t *space = msgb_put(msgb, 2);
+ space[0] = word >> 8 & 0xFF;
+ space[1] = word & 0xFF;
+}
+static inline void msgb_put_u32(struct msgb *msgb, uint32_t word)
+{
+ uint8_t *space = msgb_put(msgb, 4);
+ space[0] = word >> 24 & 0xFF;
+ space[1] = word >> 16 & 0xFF;
+ space[2] = word >> 8 & 0xFF;
+ space[3] = word & 0xFF;
+}
+static inline unsigned char *msgb_get(struct msgb *msgb, unsigned int len)
+{
+ unsigned char *tmp = msgb->data;
+ msgb->data += len;
+ msgb->len -= len;
+ return tmp;
+}
+static inline uint8_t msgb_get_u8(struct msgb *msgb)
+{
+ uint8_t *space = msgb_get(msgb, 1);
+ return space[0];
+}
+static inline uint16_t msgb_get_u16(struct msgb *msgb)
+{
+ uint8_t *space = msgb_get(msgb, 2);
+ return space[0] << 8 | space[1];
+}
+static inline uint32_t msgb_get_u32(struct msgb *msgb)
+{
+ uint8_t *space = msgb_get(msgb, 4);
+ return space[0] << 24 | space[1] << 16 | space[2] << 8 | space[3];
+}
+static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len)
+{
+ if (msgb_headroom(msgb) < (int) len)
+ MSGB_ABORT(msgb, "Not enough headroom msgb_push (%u < %u)\n",
+ msgb_headroom(msgb), len);
+ msgb->data -= len;
+ msgb->len += len;
+ return msgb->data;
+}
+static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len)
+{
+ msgb->len -= len;
+ return msgb->data += len;
+}
+
+/* increase the headroom of an empty msgb, reducing the tailroom */
+static inline void msgb_reserve(struct msgb *msg, int len)
+{
+ msg->data += len;
+ msg->tail += len;
+}
+
+static inline struct msgb *msgb_alloc_headroom(int size, int headroom,
+ const char *name)
+{
++ osmo_static_assert(size > headroom, headroom_bigger);
+
+ struct msgb *msg = msgb_alloc(size, name);
+ if (msg)
+ msgb_reserve(msg, headroom);
+ return msg;
+}
+
+/* non inline functions to ease binding */
+uint8_t *msgb_data(const struct msgb *msg);
+uint16_t msgb_length(const struct msgb *msg);
+
+
+#endif /* _MSGB_H */
--- /dev/null
- #include "linuxlist.h"
+/*
+ * (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 MSG_FILE_H
+#define MSG_FILE_H
+
- struct msg_entry {
++#include <osmocom/core/linuxlist.h>
+
+/**
+ * One message in the list.
+ */
- struct msg_entries {
++struct osmo_config_entry {
+ struct llist_head list;
+
+ /* number for everyone to use */
+ int nr;
+
+ /* data from the file */
+ char *mcc;
+ char *mnc;
+ char *option;
+ char *text;
+};
+
- struct msg_entries *msg_entry_parse(void *ctx, const char *filename);
++struct osmo_config_list {
+ struct llist_head entry;
+};
+
++struct osmo_config_list* osmo_config_list_parse(void *ctx, const char *filename);
+
+#endif
--- /dev/null
- void osmo_panic(const char *fmt, ...);
- void osmo_set_panic_handler(osmo_panic_handler_t h);
+#ifndef OSMOCORE_PANIC_H
+#define OSMOCORE_PANIC_H
+
+#include <stdarg.h>
+
+typedef void (*osmo_panic_handler_t)(const char *fmt, va_list args);
+
++extern void osmo_panic(const char *fmt, ...);
++extern void osmo_set_panic_handler(osmo_panic_handler_t h);
+
+#endif
--- /dev/null
- int plugin_load_all(const char *directory);
+#ifndef _OSMO_PLUGIN_H
+#define _OSMO_PLUGIN_H
+
++int osmo_plugin_load_all(const char *directory);
+
+#endif
--- /dev/null
- struct bsc_fd {
+#ifndef _BSC_SELECT_H
+#define _BSC_SELECT_H
+
+#include <osmocom/core/linuxlist.h>
+
+#define BSC_FD_READ 0x0001
+#define BSC_FD_WRITE 0x0002
+#define BSC_FD_EXCEPT 0x0004
+
- int (*cb)(struct bsc_fd *fd, unsigned int what);
++struct osmo_fd {
+ struct llist_head list;
+ int fd;
+ unsigned int when;
- int bsc_register_fd(struct bsc_fd *fd);
- void bsc_unregister_fd(struct bsc_fd *fd);
- int bsc_select_main(int polling);
++ int (*cb)(struct osmo_fd *fd, unsigned int what);
+ void *data;
+ unsigned int priv_nr;
+};
+
++int osmo_fd_register(struct osmo_fd *fd);
++void osmo_fd_unregister(struct osmo_fd *fd);
++int osmo_select_main(int polling);
+#endif /* _BSC_SELECT_H */
--- /dev/null
- #ifndef OSMOCORE_SIGNAL_H
- #define OSMOCORE_SIGNAL_H
++#ifndef OSMO_SIGNAL_H
++#define OSMO_SIGNAL_H
+
- typedef int signal_cbfn(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data);
++typedef int osmo_signal_cbfn(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data);
+
+
+/* Management */
- int register_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data);
- void unregister_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data);
++int osmo_signal_register_handler(unsigned int subsys, osmo_signal_cbfn *cbfn, void *data);
++void osmo_signal_unregister_handler(unsigned int subsys, osmo_signal_cbfn *cbfn, void *data);
+
+/* Dispatch */
- void dispatch_signal(unsigned int subsys, unsigned int signal, void *signal_data);
++void osmo_signal_dispatch(unsigned int subsys, unsigned int signal, void *signal_data);
+
- #endif /* OSMOCORE_SIGNAL_H */
++#endif /* OSMO_SIGNAL_H */
--- /dev/null
- struct counter {
+#ifndef _STATISTICS_H
+#define _STATISTICS_H
+
- static inline void counter_inc(struct counter *ctr)
++struct osmo_counter {
+ struct llist_head list;
+ const char *name;
+ const char *description;
+ unsigned long value;
+};
+
- static inline unsigned long counter_get(struct counter *ctr)
++static inline void osmo_counter_inc(struct osmo_counter *ctr)
+{
+ ctr->value++;
+}
+
- static inline void counter_reset(struct counter *ctr)
++static inline unsigned long osmo_counter_get(struct osmo_counter *ctr)
+{
+ return ctr->value;
+}
+
- struct counter *counter_alloc(const char *name);
- void counter_free(struct counter *ctr);
++static inline void osmo_counter_reset(struct osmo_counter *ctr)
+{
+ ctr->value = 0;
+}
+
- int counters_for_each(int (*handle_counter)(struct counter *, void *), void *data);
++struct osmo_counter *osmo_counter_alloc(const char *name);
++void osmo_counter_free(struct osmo_counter *ctr);
+
- struct counter *counter_get_by_name(const char *name);
++int osmo_counters_for_each(int (*handle_counter)(struct osmo_counter *, void *), void *data);
+
++struct osmo_counter *osmo_counter_get_by_name(const char *name);
+
+#endif /* _STATISTICS_H */
--- /dev/null
- * - Create a struct timer_list
+/*
+ * (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.
+ *
+ */
+
+#ifndef TIMER_H
+#define TIMER_H
+
+#include <sys/time.h>
+
+#include <osmocom/core/linuxlist.h>
+
+/**
+ * Timer management:
- struct timer_list {
++ * - Create a struct osmo_timer_list
+ * - Fill out timeout and use add_timer or
+ * use schedule_timer to schedule a timer in
+ * x seconds and microseconds from now...
+ * - Use del_timer to remove the timer
+ *
+ * Internally:
+ * - We hook into select.c to give a timeval of the
+ * nearest timer. On already passed timers we give
+ * it a 0 to immediately fire after the select
+ * - update_timers will call the callbacks and remove
+ * the timers.
+ *
+ */
- void bsc_add_timer(struct timer_list *timer);
- void bsc_schedule_timer(struct timer_list *timer, int seconds, int microseconds);
- void bsc_del_timer(struct timer_list *timer);
- int bsc_timer_pending(struct timer_list *timer);
++struct osmo_timer_list {
+ struct llist_head entry;
+ struct timeval timeout;
+ unsigned int active : 1;
+ unsigned int handled : 1;
+ unsigned int in_list : 1;
+
+ void (*cb)(void*);
+ void *data;
+};
+
+/**
+ * timer management
+ */
- struct timeval *bsc_nearest_timer();
- void bsc_prepare_timers();
- int bsc_update_timers();
- int bsc_timer_check(void);
++void osmo_timer_add(struct osmo_timer_list *timer);
++void osmo_timer_schedule(struct osmo_timer_list *timer, int seconds, int microseconds);
++void osmo_timer_del(struct osmo_timer_list *timer);
++int osmo_timer_pending(struct osmo_timer_list *timer);
+
+
+/**
+ * internal timer list management
+ */
++struct timeval *osmo_timers_nearest();
++void osmo_timers_prepare();
++int osmo_timers_update();
++int osmo_timers_check(void);
+
+#endif
--- /dev/null
- char bcd2char(uint8_t bcd);
+#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);
+
- uint8_t char2bcd(char c);
++char osmo_bcd2char(uint8_t bcd);
+/* only works for numbers in ascci */
- int hexparse(const char *str, uint8_t *b, int max_len);
- char *hexdump(const unsigned char *buf, int len);
- char *hexdump_nospc(const unsigned char *buf, int len);
- char *ubit_dump(const uint8_t *bits, unsigned int len);
++uint8_t osmo_char2bcd(char c);
+
- #define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
++int osmo_hexparse(const char *str, uint8_t *b, int max_len);
++char *osmo_hexdump(const unsigned char *buf, int len);
++char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len);
++char *osmo_ubit_dump(const uint8_t *bits, unsigned int len);
+
++#define osmo_static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
+
+void osmo_str2lower(char *out, const char *in);
+void osmo_str2upper(char *out, const char *in);
+
+#define OSMO_SNPRINTF_RET(ret, rem, offset, len) \
+do { \
+ len += ret; \
+ if (ret > rem) \
+ ret = rem; \
+ offset += ret; \
+ rem -= ret; \
+} while (0)
+
+#endif
--- /dev/null
- #ifndef write_queue_h
- #define write_queue_h
+/* 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.
+ *
+ */
- struct write_queue {
- struct bsc_fd bfd;
++#ifndef OSMO_WQUEUE_H
++#define OSMO_WQUEUE_H
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/msgb.h>
+
- int (*read_cb)(struct bsc_fd *fd);
- int (*write_cb)(struct bsc_fd *fd, struct msgb *msg);
- int (*except_cb)(struct bsc_fd *fd);
++struct osmo_wqueue {
++ struct osmo_fd bfd;
+ unsigned int max_length;
+ unsigned int current_length;
+
+ struct llist_head msg_queue;
+
- void write_queue_init(struct write_queue *queue, int max_length);
- void write_queue_clear(struct write_queue *queue);
- int write_queue_enqueue(struct write_queue *queue, struct msgb *data);
- int write_queue_bfd_cb(struct bsc_fd *fd, unsigned int what);
++ int (*read_cb)(struct osmo_fd *fd);
++ int (*write_cb)(struct osmo_fd *fd, struct msgb *msg);
++ int (*except_cb)(struct osmo_fd *fd);
+};
+
++void osmo_wqueue_init(struct osmo_wqueue *queue, int max_length);
++void osmo_wqueue_clear(struct osmo_wqueue *queue);
++int osmo_wqueue_enqueue(struct osmo_wqueue *queue, struct msgb *data);
++int osmo_wqueue_bfd_cb(struct osmo_fd *fd, unsigned int what);
+
+#endif
--- /dev/null
- osmogsm_HEADERS = comp128.h gsm0808.h gsm48_ie.h mncc.h rxlev_stat.h \
++osmogsm_HEADERS = a5.h comp128.h gsm0808.h gsm48_ie.h mncc.h rxlev_stat.h \
+ gsm0480.h gsm48.h gsm_utils.h rsl.h tlv.h
+
+SUBDIRS = protocol
+
+osmogsmdir = $(includedir)/osmocom/gsm
--- /dev/null
--- /dev/null
++/*
++ * a5.h
++ *
++ * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
++ *
++ * 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 __OSMO_A5_H__
++#define __OSMO_A5_H__
++
++#include <stdint.h>
++
++#include <osmocom/core/bits.h>
++
++static inline uint32_t
++osmo_a5_fn_count(uint32_t fn)
++{
++ int t1 = fn / (26 * 51);
++ int t2 = fn % 26;
++ int t3 = fn % 51;
++ return (t1 << 11) | (t3 << 5) | t2;
++}
++
++ /* Notes:
++ * - key must be 8 bytes long (or NULL for A5/0)
++ * - the dl and ul pointer must be either NULL or 114 bits long
++ * - fn is the _real_ GSM frame number.
++ * (converted internally to fn_count)
++ */
++void osmo_a5(int n, uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul);
++void osmo_a5_1(uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul);
++void osmo_a5_2(uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul);
++
++#endif /* __OSMO_A5_H__ */
--- /dev/null
- struct bsc_fd fd;
+/* minimalistic telnet/network interface it might turn into a wire interface */
+/* (C) 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.
+ *
+ */
+
+#ifndef TELNET_INTERFACE_H
+#define TELNET_INTERFACE_H
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/select.h>
+
+#include <osmocom/vty/vty.h>
+
+struct telnet_connection {
+ struct llist_head entry;
+ void *priv;
++ struct osmo_fd fd;
+ struct vty *vty;
+ struct log_target *dbg;
+};
+
+
+int telnet_init(void *tall_ctx, void *priv, int port);
+
+#endif
--- /dev/null
- LIBVERSION=0:0:0
+SUBDIRS=. vty codec gsm
+
+# 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
- process.c
++LIBVERSION=1:0:1
+
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+AM_CFLAGS = -fPIC -Wall
+
+lib_LTLIBRARIES = libosmocore.la
+
+libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c bits.c \
+ bitvec.c statistics.c \
+ write_queue.c utils.c \
+ logging.c logging_syslog.c rate_ctr.c \
+ gsmtap_util.c crc16.c panic.c backtrace.c \
- libosmocore_la_LDFLAGS = -ldl
++ process.c conv.c application.c
+
+if ENABLE_PLUGIN
+libosmocore_la_SOURCES += plugin.c
++libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) -ldl
++else
++libosmocore_la_LDFLAGS = -version-info $(LIBVERSION)
+endif
+
+if ENABLE_TALLOC
+libosmocore_la_SOURCES += talloc.c
+endif
+
+if ENABLE_MSGFILE
+libosmocore_la_SOURCES += msgfile.c
+endif
--- /dev/null
--- /dev/null
++/* Utility functions to setup applications */
++/*
++ * (C) 2011 by Holger Hans Peter Freyther
++ *
++ * 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 <osmocom/core/application.h>
++#include <osmocom/core/logging.h>
++
++#include <signal.h>
++
++struct log_target *osmo_stderr_target;
++
++void osmo_init_ignore_signals(void)
++{
++ /* Signals that by default would terminate */
++ signal(SIGPIPE, SIG_IGN);
++ signal(SIGALRM, SIG_IGN);
++ signal(SIGHUP, SIG_IGN);
++ signal(SIGIO, SIG_IGN);
++}
++
++int osmo_init_logging(const struct log_info *log_info)
++{
++ log_init(log_info);
++ osmo_stderr_target = log_target_create_stderr();
++ if (!osmo_stderr_target)
++ return -1;
++
++ log_add_target(osmo_stderr_target);
++ log_set_all_filter(osmo_stderr_target, 1);
++ return 0;
++}
--- /dev/null
- void generate_backtrace()
+/*
+ * (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>
+ * (C) 2010 by Nico Golde <nico@ngolde.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <osmocom/core/utils.h>
+#include "config.h"
+
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
++void osmo_generate_backtrace()
+{
+ int i, nptrs;
+ void *buffer[100];
+ char **strings;
+
+ nptrs = backtrace(buffer, ARRAY_SIZE(buffer));
+ printf("backtrace() returned %d addresses\n", nptrs);
+
+ strings = backtrace_symbols(buffer, nptrs);
+ if (!strings)
+ return;
+
+ for (i = 1; i < nptrs; i++)
+ printf("%s\n", strings[i]);
+
+ free(strings);
+}
+#endif
--- /dev/null
+# 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 = libosmocodec.la
+
+libosmocodec_la_SOURCES = gsm610.c gsm620.c gsm660.c gsm690.c
++libosmocodec_la_LDFALGS = -version-info $(LIBVERSION)
--- /dev/null
--- /dev/null
++/*
++ * conv.c
++ *
++ * Generic convolutional encoding / decoding
++ *
++ * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
++ *
++ * 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 <alloca.h>
++#include <stdint.h>
++#include <stdlib.h>
++#include <string.h>
++
++#include <osmocom/core/bits.h>
++#include <osmocom/core/conv.h>
++
++
++/* ------------------------------------------------------------------------ */
++/* Encoding */
++/* ------------------------------------------------------------------------ */
++
++void
++osmo_conv_encode_init(struct osmo_conv_encoder *encoder,
++ const struct osmo_conv_code *code)
++{
++ memset(encoder, 0x00, sizeof(struct osmo_conv_encoder));
++ encoder->code = code;
++}
++
++static inline int
++_conv_encode_do_output(struct osmo_conv_encoder *encoder,
++ uint8_t out, ubit_t *output)
++{
++ const struct osmo_conv_code *code = encoder->code;
++ int o_idx = 0;
++ int j;
++
++ if (code->puncture) {
++ for (j=0; j<code->N; j++)
++ {
++ int bit_no = code->N - j - 1;
++ int r_idx = encoder->i_idx * code->N + j;
++
++ if (code->puncture[encoder->p_idx] == r_idx)
++ encoder->p_idx++;
++ else
++ output[o_idx++] = (out >> bit_no) & 1;
++ }
++ } else {
++ for (j=0; j<code->N; j++)
++ {
++ int bit_no = code->N - j - 1;
++ output[o_idx++] = (out >> bit_no) & 1;
++ }
++ }
++
++ return o_idx;
++}
++
++int
++osmo_conv_encode_raw(struct osmo_conv_encoder *encoder,
++ const ubit_t *input, ubit_t *output, int n)
++{
++ const struct osmo_conv_code *code = encoder->code;
++ uint8_t state;
++ int i;
++ int o_idx;
++
++ o_idx = 0;
++ state = encoder->state;
++
++ for (i=0; i<n; i++) {
++ int bit = input[i];
++ uint8_t out;
++
++ out = code->next_output[state][bit];
++ state = code->next_state[state][bit];
++
++ o_idx += _conv_encode_do_output(encoder, out, &output[o_idx]);
++
++ encoder->i_idx++;
++ }
++
++ encoder->state = state;
++
++ return o_idx;
++}
++
++int
++osmo_conv_encode_finish(struct osmo_conv_encoder *encoder,
++ ubit_t *output)
++{
++ const struct osmo_conv_code *code = encoder->code;
++ uint8_t state;
++ int n;
++ int i;
++ int o_idx;
++
++ n = code->K - 1;
++
++ o_idx = 0;
++ state = encoder->state;
++
++ for (i=0; i<n; i++) {
++ uint8_t out;
++
++ if (code->next_term_output) {
++ out = code->next_term_output[state];
++ state = code->next_term_state[state];
++ } else {
++ out = code->next_output[state][0];
++ state = code->next_state[state][0];
++ }
++
++ o_idx += _conv_encode_do_output(encoder, out, &output[o_idx]);
++
++ encoder->i_idx++;
++ }
++
++ encoder->state = state;
++
++ return o_idx;
++}
++
++int
++osmo_conv_encode(const struct osmo_conv_code *code,
++ const ubit_t *input, ubit_t *output)
++{
++ struct osmo_conv_encoder encoder;
++ int l;
++
++ osmo_conv_encode_init(&encoder, code);
++ l = osmo_conv_encode_raw(&encoder, input, output, code->len);
++ l += osmo_conv_encode_finish(&encoder, &output[l]);
++
++ return l;
++}
++
++
++/* ------------------------------------------------------------------------ */
++/* Decoding (viterbi) */
++/* ------------------------------------------------------------------------ */
++
++#define MAX_AE 0x00ffffff
++
++void
++osmo_conv_decode_init(struct osmo_conv_decoder *decoder,
++ const struct osmo_conv_code *code, int len)
++{
++ int n_states;
++
++ /* Init */
++ if (len <= 0)
++ len = code->len;
++
++ n_states = 1 << (code->K - 1);
++
++ memset(decoder, 0x00, sizeof(struct osmo_conv_decoder));
++
++ decoder->code = code;
++ decoder->n_states = n_states;
++ decoder->len = len;
++
++ /* Allocate arrays */
++ decoder->ae = malloc(sizeof(unsigned int) * n_states);
++ decoder->ae_next = malloc(sizeof(unsigned int) * n_states);
++
++ decoder->state_history = malloc(sizeof(uint8_t) * n_states * (len + decoder->code->K - 1));
++
++ /* Classic reset */
++ osmo_conv_decode_reset(decoder);
++}
++
++void
++osmo_conv_decode_reset(struct osmo_conv_decoder *decoder)
++{
++ int i;
++
++ /* Reset indexes */
++ decoder->o_idx = 0;
++ decoder->p_idx = 0;
++
++ /* Initial error (only state 0 is valid) */
++ decoder->ae[0] = 0;
++ for (i=1; i<decoder->n_states; i++) {
++ decoder->ae[i] = MAX_AE;
++ }
++}
++
++void
++osmo_conv_decode_deinit(struct osmo_conv_decoder *decoder)
++{
++ free(decoder->ae);
++ free(decoder->ae_next);
++ free(decoder->state_history);
++
++ memset(decoder, 0x00, sizeof(struct osmo_conv_decoder));
++}
++
++int
++osmo_conv_decode_scan(struct osmo_conv_decoder *decoder,
++ const sbit_t *input, int n)
++{
++ const struct osmo_conv_code *code = decoder->code;
++
++ int i, s, b, j;
++
++ int n_states;
++ unsigned int *ae;
++ unsigned int *ae_next;
++ uint8_t *state_history;
++ sbit_t *in_sym;
++
++ int i_idx, p_idx;
++
++ /* Prepare */
++ n_states = decoder->n_states;
++
++ ae = decoder->ae;
++ ae_next = decoder->ae_next;
++ state_history = &decoder->state_history[n_states * decoder->o_idx];
++
++ in_sym = alloca(sizeof(sbit_t) * code->N);
++
++ i_idx = 0;
++ p_idx = decoder->p_idx;
++
++ /* Scan the treillis */
++ for (i=0; i<n; i++)
++ {
++ /* Reset next accumulated error */
++ for (s=0; s<n_states; s++) {
++ ae_next[s] = MAX_AE;
++ }
++
++ /* Get input */
++ if (code->puncture) {
++ /* Hard way ... */
++ for (j=0; j<code->N; j++) {
++ int idx = ((decoder->o_idx + i) * code->N) + j;
++ if (idx == code->puncture[p_idx]) {
++ in_sym[j] = 0; /* Undefined */
++ p_idx++;
++ } else {
++ in_sym[j] = input[i_idx];
++ i_idx++;
++ }
++ }
++ } else {
++ /* Easy, just copy N bits */
++ memcpy(in_sym, &input[i_idx], code->N);
++ i_idx += code->N;
++ }
++
++ /* Scan all state */
++ for (s=0; s<n_states; s++)
++ {
++ /* Scan possible input bits */
++ for (b=0; b<2; b++)
++ {
++ int nae, ov, e;
++ uint8_t m;
++
++ /* Next output and state */
++ uint8_t out = code->next_output[s][b];
++ uint8_t state = code->next_state[s][b];
++
++ /* New error for this path */
++ nae = ae[s]; /* start from last error */
++ m = 1 << (code->N - 1); /* mask for 'out' bit selection */
++
++ for (j=0; j<code->N; j++) {
++ ov = (out & m) ? -127 : 127; /* sbit_t value for it */
++ e = ((int)in_sym[j]) - ov; /* raw error for this bit */
++ nae += (e * e) >> 9; /* acc the squared/scaled value */
++ m >>= 1; /* next mask bit */
++ }
++
++ /* Is it survivor ? */
++ if (ae_next[state] > nae) {
++ ae_next[state] = nae;
++ state_history[(n_states * i) + state] = s;
++ }
++ }
++ }
++
++ /* Copy accumulated error */
++ memcpy(ae, ae_next, sizeof(unsigned int) * n_states);
++ }
++
++ /* Update decoder state */
++ decoder->p_idx = p_idx;
++ decoder->o_idx += n;
++
++ return i_idx;
++}
++
++int
++osmo_conv_decode_finish(struct osmo_conv_decoder *decoder,
++ const sbit_t *input)
++{
++ const struct osmo_conv_code *code = decoder->code;
++
++ int i, s, j;
++
++ int n_states;
++ unsigned int *ae;
++ unsigned int *ae_next;
++ uint8_t *state_history;
++ sbit_t *in_sym;
++
++ int i_idx, p_idx;
++
++ /* Prepare */
++ n_states = decoder->n_states;
++
++ ae = decoder->ae;
++ ae_next = decoder->ae_next;
++ state_history = &decoder->state_history[n_states * decoder->o_idx];
++
++ in_sym = alloca(sizeof(sbit_t) * code->N);
++
++ i_idx = 0;
++ p_idx = decoder->p_idx;
++
++ /* Scan the treillis */
++ for (i=0; i<code->K-1; i++)
++ {
++ /* Reset next accumulated error */
++ for (s=0; s<n_states; s++) {
++ ae_next[s] = MAX_AE;
++ }
++
++ /* Get input */
++ if (code->puncture) {
++ /* Hard way ... */
++ for (j=0; j<code->N; j++) {
++ int idx = ((decoder->o_idx + i) * code->N) + j;
++ if (idx == code->puncture[p_idx]) {
++ in_sym[j] = 0; /* Undefined */
++ p_idx++;
++ } else {
++ in_sym[j] = input[i_idx];
++ i_idx++;
++ }
++ }
++ } else {
++ /* Easy, just copy N bits */
++ memcpy(in_sym, &input[i_idx], code->N);
++ i_idx += code->N;
++ }
++
++ /* Scan all state */
++ for (s=0; s<n_states; s++)
++ {
++ int nae, ov, e;
++ uint8_t m;
++
++ /* Next output and state */
++ uint8_t out;
++ uint8_t state;
++
++ if (code->next_term_output) {
++ out = code->next_term_output[s];
++ state = code->next_term_state[s];
++ } else {
++ out = code->next_output[s][0];
++ state = code->next_state[s][0];
++ }
++
++ /* New error for this path */
++ nae = ae[s]; /* start from last error */
++ m = 1 << (code->N - 1); /* mask for 'out' bit selection */
++
++ for (j=0; j<code->N; j++) {
++ int is = (int)in_sym[j];
++ if (is) {
++ ov = (out & m) ? -127 : 127; /* sbit_t value for it */
++ e = is - ov; /* raw error for this bit */
++ nae += (e * e) >> 9; /* acc the squared/scaled value */
++ }
++ m >>= 1; /* next mask bit */
++ }
++
++ /* Is it survivor ? */
++ if (ae_next[state] > nae) {
++ ae_next[state] = nae;
++ state_history[(n_states * i) + state] = s;
++ }
++ }
++
++ /* Copy accumulated error */
++ memcpy(ae, ae_next, sizeof(unsigned int) * n_states);
++ }
++
++ /* Update decoder state */
++ decoder->p_idx = p_idx;
++ decoder->o_idx += code->K - 1;
++
++ return i_idx;
++}
++
++int
++osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder,
++ ubit_t *output, int has_finish)
++{
++ const struct osmo_conv_code *code = decoder->code;
++
++ int min_ae;
++ uint8_t min_state, cur_state;
++ int i, s, n;
++
++ uint8_t *sh_ptr;
++
++ /* Find state with least error */
++ min_ae = MAX_AE;
++ min_state = 0xff;
++
++ for (s=0; s<decoder->n_states; s++)
++ {
++ if (decoder->ae[s] < min_ae) {
++ min_ae = decoder->ae[s];
++ min_state = s;
++ }
++ }
++
++ if (min_state == 0xff)
++ return -1;
++
++ /* Traceback */
++ cur_state = min_state;
++
++ n = decoder->o_idx;
++
++ sh_ptr = &decoder->state_history[decoder->n_states * (n-1)];
++
++ /* No output for the K-1 termination input bits */
++ if (has_finish) {
++ for (i=0; i<code->K-1; i++) {
++ cur_state = sh_ptr[cur_state];
++ sh_ptr -= decoder->n_states;
++ }
++ n -= code->K - 1;
++ }
++
++ /* Generate output backward */
++ for (i=n-1; i>=0; i--)
++ {
++ min_state = cur_state;
++ cur_state = sh_ptr[cur_state];
++
++ sh_ptr -= decoder->n_states;
++
++ if (code->next_state[cur_state][0] == min_state)
++ output[i] = 0;
++ else
++ output[i] = 1;
++ }
++
++ return min_ae;
++}
++
++int
++osmo_conv_decode(const struct osmo_conv_code *code,
++ const sbit_t *input, ubit_t *output)
++{
++ struct osmo_conv_decoder decoder;
++ int rv, l;
++
++ osmo_conv_decode_init(&decoder, code, 0);
++
++ l = osmo_conv_decode_scan(&decoder, input, code->len);
++ l = osmo_conv_decode_finish(&decoder, &input[l]);
++
++ rv = osmo_conv_decode_get_output(&decoder, output, 1);
++
++ osmo_conv_decode_deinit(&decoder);
++
++ return rv;
++}
--- /dev/null
- uint16_t const crc16_table[256] = {
+/*
+ * This was copied from the linux kernel and adjusted for our types.
+ */
+/*
+ * crc16.c
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <osmocom/core/crc16.h>
+
+/** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */
- uint16_t crc16(uint16_t crc, uint8_t const *buffer, size_t len)
++uint16_t const osmo_crc16_table[256] = {
+ 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
+ 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
+ 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
+ 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
+ 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
+ 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
+ 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
+ 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
+ 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
+ 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
+ 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
+ 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
+ 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
+ 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
+ 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
+ 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
+ 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
+ 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
+ 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
+ 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
+ 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
+ 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
+ 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
+ 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
+ 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
+ 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
+ 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
+ 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
+ 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
+ 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
+ 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
+ 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
+};
+
+/**
+ * crc16 - compute the CRC-16 for the data buffer
+ * @crc: previous CRC value
+ * @buffer: data pointer
+ * @len: number of bytes in the buffer
+ *
+ * Returns the updated CRC value.
+ */
- crc = crc16_byte(crc, *buffer++);
++uint16_t osmo_crc16(uint16_t crc, uint8_t const *buffer, size_t len)
+{
+ while (len--)
++ crc = osmo_crc16_byte(crc, *buffer++);
+ return crc;
+}
--- /dev/null
- libosmogsm_la_SOURCES = rxlev_stat.c tlv_parser.c comp128.c gsm_utils.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 = libosmogsm.la
+
++libosmogsm_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c gsm_utils.c \
+ rsl.c gsm48.c gsm48_ie.c gsm0808.c \
+ gprs_cipher_core.c gsm0480.c
++libosmogsm_la_LDFLAGS = -version-info $(LIBVERSION)
+libosmogsm_la_LIBADD = $(top_builddir)/src/libosmocore.la
--- /dev/null
--- /dev/null
++/*
++ * a5.c
++ *
++ * Full reimplementation of A5/1,2 (split and threadsafe)
++ *
++ * The logic behind the algorithm is taken from "A pedagogical implementation
++ * of the GSM A5/1 and A5/2 "voice privacy" encryption algorithms." by
++ * Marc Briceno, Ian Goldberg, and David Wagner.
++ *
++ * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
++ *
++ * 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 <string.h>
++
++#include <osmocom/gsm/a5.h>
++
++void
++osmo_a5(int n, uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
++{
++ switch (n)
++ {
++ case 0:
++ if (dl)
++ memset(dl, 0x00, 114);
++ if (ul)
++ memset(ul, 0x00, 114);
++ break;
++
++ case 1:
++ osmo_a5_1(key, fn, dl, ul);
++ break;
++
++ case 2:
++ osmo_a5_2(key, fn, dl, ul);
++ break;
++
++ default:
++ /* a5/[3..7] not supported here/yet */
++ break;
++ }
++}
++
++
++/* ------------------------------------------------------------------------ */
++/* A5/1&2 common stuff */
++/* ------------------------------------------------------------------------ */
++
++#define A5_R1_LEN 19
++#define A5_R2_LEN 22
++#define A5_R3_LEN 23
++#define A5_R4_LEN 17 /* A5/2 only */
++
++#define A5_R1_MASK ((1<<A5_R1_LEN)-1)
++#define A5_R2_MASK ((1<<A5_R2_LEN)-1)
++#define A5_R3_MASK ((1<<A5_R3_LEN)-1)
++#define A5_R4_MASK ((1<<A5_R4_LEN)-1)
++
++#define A5_R1_TAPS 0x072000 /* x^19 + x^5 + x^2 + x + 1 */
++#define A5_R2_TAPS 0x300000 /* x^22 + x + 1 */
++#define A5_R3_TAPS 0x700080 /* x^23 + x^15 + x^2 + x + 1 */
++#define A5_R4_TAPS 0x010800 /* x^17 + x^5 + 1 */
++
++static inline uint32_t
++_a5_12_parity(uint32_t x)
++{
++ x ^= x >> 16;
++ x ^= x >> 8;
++ x ^= x >> 4;
++ x ^= x >> 2;
++ x ^= x >> 1;
++ return x & 1;
++}
++
++static inline uint32_t
++_a5_12_majority(uint32_t v1, uint32_t v2, uint32_t v3)
++{
++ return (!!v1 + !!v2 + !!v3) >= 2;
++}
++
++static inline uint32_t
++_a5_12_clock(uint32_t r, uint32_t mask, uint32_t taps)
++{
++ return ((r << 1) & mask) | _a5_12_parity(r & taps);
++}
++
++
++/* ------------------------------------------------------------------------ */
++/* A5/1 */
++/* ------------------------------------------------------------------------ */
++
++#define A51_R1_CLKBIT 0x000100
++#define A51_R2_CLKBIT 0x000400
++#define A51_R3_CLKBIT 0x000400
++
++static inline void
++_a5_1_clock(uint32_t r[], int force)
++{
++ int cb[3], maj;
++
++ cb[0] = !!(r[0] & A51_R1_CLKBIT);
++ cb[1] = !!(r[1] & A51_R2_CLKBIT);
++ cb[2] = !!(r[2] & A51_R3_CLKBIT);
++
++ maj = _a5_12_majority(cb[0], cb[1], cb[2]);
++
++ if (force || (maj == cb[0]))
++ r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
++
++ if (force || (maj == cb[1]))
++ r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
++
++ if (force || (maj == cb[2]))
++ r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
++}
++
++static inline uint8_t
++_a5_1_get_output(uint32_t r[])
++{
++ return (r[0] >> (A5_R1_LEN-1)) ^
++ (r[1] >> (A5_R2_LEN-1)) ^
++ (r[2] >> (A5_R3_LEN-1));
++}
++
++void
++osmo_a5_1(uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
++{
++ uint32_t r[3] = {0, 0, 0};
++ uint32_t fn_count;
++ uint32_t b;
++ int i;
++
++ /* Key load */
++ for (i=0; i<64; i++)
++ {
++ b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
++
++ _a5_1_clock(r, 1);
++
++ r[0] ^= b;
++ r[1] ^= b;
++ r[2] ^= b;
++ }
++
++ /* Frame count load */
++ fn_count = osmo_a5_fn_count(fn);
++
++ for (i=0; i<22; i++)
++ {
++ b = (fn_count >> i) & 1;
++
++ _a5_1_clock(r, 1);
++
++ r[0] ^= b;
++ r[1] ^= b;
++ r[2] ^= b;
++ }
++
++ /* Mix */
++ for (i=0; i<100; i++)
++ {
++ _a5_1_clock(r, 0);
++ }
++
++ /* Output */
++ for (i=0; i<114; i++) {
++ _a5_1_clock(r, 0);
++ if (dl)
++ dl[i] = _a5_1_get_output(r);
++ }
++
++ for (i=0; i<114; i++) {
++ _a5_1_clock(r, 0);
++ if (ul)
++ ul[i] = _a5_1_get_output(r);
++ }
++}
++
++
++/* ------------------------------------------------------------------------ */
++/* A5/2 */
++/* ------------------------------------------------------------------------ */
++
++#define A52_R4_CLKBIT0 0x000400
++#define A52_R4_CLKBIT1 0x000008
++#define A52_R4_CLKBIT2 0x000080
++
++static inline void
++_a5_2_clock(uint32_t r[], int force)
++{
++ int cb[3], maj;
++
++ cb[0] = !!(r[3] & A52_R4_CLKBIT0);
++ cb[1] = !!(r[3] & A52_R4_CLKBIT1);
++ cb[2] = !!(r[3] & A52_R4_CLKBIT2);
++
++ maj = (cb[0] + cb[1] + cb[2]) >= 2;
++
++ if (force || (maj == cb[0]))
++ r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
++
++ if (force || (maj == cb[1]))
++ r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
++
++ if (force || (maj == cb[2]))
++ r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
++
++ r[3] = _a5_12_clock(r[3], A5_R4_MASK, A5_R4_TAPS);
++}
++
++static inline uint8_t
++_a5_2_get_output(uint32_t r[], uint8_t *db)
++{
++ uint8_t cb, tb;
++
++ tb = (r[0] >> (A5_R1_LEN-1)) ^
++ (r[1] >> (A5_R2_LEN-1)) ^
++ (r[2] >> (A5_R3_LEN-1));
++
++ cb = *db;
++
++ *db = ( tb ^
++ _a5_12_majority( r[0] & 0x08000, ~r[0] & 0x04000, r[0] & 0x1000) ^
++ _a5_12_majority(~r[1] & 0x10000, r[1] & 0x02000, r[1] & 0x0200) ^
++ _a5_12_majority( r[2] & 0x40000, r[2] & 0x10000, ~r[2] & 0x2000)
++ );
++
++ return cb;
++}
++
++void
++osmo_a5_2(uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
++{
++ uint32_t r[4] = {0, 0, 0, 0};
++ uint32_t fn_count;
++ uint32_t b;
++ uint8_t db = 0, o;
++ int i;
++
++ /* Key load */
++ for (i=0; i<64; i++)
++ {
++ b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
++
++ _a5_2_clock(r, 1);
++
++ r[0] ^= b;
++ r[1] ^= b;
++ r[2] ^= b;
++ r[3] ^= b;
++ }
++
++ /* Frame count load */
++ fn_count = osmo_a5_fn_count(fn);
++
++ for (i=0; i<22; i++)
++ {
++ b = (fn_count >> i) & 1;
++
++ _a5_2_clock(r, 1);
++
++ r[0] ^= b;
++ r[1] ^= b;
++ r[2] ^= b;
++ r[3] ^= b;
++ }
++
++ r[0] |= 1 << 15;
++ r[1] |= 1 << 16;
++ r[2] |= 1 << 18;
++ r[3] |= 1 << 10;
++
++ /* Mix */
++ for (i=0; i<100; i++)
++ {
++ _a5_2_clock(r, 0);
++ }
++
++ _a5_2_get_output(r, &db);
++
++
++ /* Output */
++ for (i=0; i<114; i++) {
++ _a5_2_clock(r, 0);
++ o = _a5_2_get_output(r, &db);
++ if (dl)
++ dl[i] = o;
++ }
++
++ for (i=0; i<114; i++) {
++ _a5_2_clock(r, 0);
++ o = _a5_2_get_output(r, &db);
++ if (ul)
++ ul[i] = o;
++ }
++}
--- /dev/null
- return plugin_load_all(path);
+/* GPRS LLC cipher core infrastructure */
+
+/* (C) 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 <errno.h>
+#include <stdint.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/plugin.h>
+
+#include <osmocom/crypt/gprs_cipher.h>
+
+static LLIST_HEAD(gprs_ciphers);
+
+static struct gprs_cipher_impl *selected_ciphers[_GPRS_ALGO_NUM];
+
+/* register a cipher with the core */
+int gprs_cipher_register(struct gprs_cipher_impl *ciph)
+{
+ if (ciph->algo > ARRAY_SIZE(selected_ciphers))
+ return -ERANGE;
+
+ llist_add_tail(&ciph->list, &gprs_ciphers);
+
+ /* check if we want to select this implementation over others */
+ if (!selected_ciphers[ciph->algo] ||
+ (selected_ciphers[ciph->algo]->priority > ciph->priority))
+ selected_ciphers[ciph->algo] = ciph;
+
+ return 0;
+}
+
+/* load all available GPRS cipher plugins */
+int gprs_cipher_load(const char *path)
+{
+ /* load all plugins available from path */
++ return osmo_plugin_load_all(path);
+}
+
+/* function to be called by core code */
+int gprs_cipher_run(uint8_t *out, uint16_t len, enum gprs_ciph_algo algo,
+ uint64_t kc, uint32_t iv, enum gprs_cipher_direction dir)
+{
+ if (algo > ARRAY_SIZE(selected_ciphers))
+ return -ERANGE;
+
+ if (!selected_ciphers[algo])
+ return -EINVAL;
+
+ if (len > GSM0464_CIPH_MAX_BLOCK)
+ return -ERANGE;
+
+ /* run the actual cipher from the plugin */
+ return selected_ciphers[algo]->run(out, len, kc, iv, dir);
+}
+
+int gprs_cipher_supported(enum gprs_ciph_algo algo)
+{
+ if (algo > ARRAY_SIZE(selected_ciphers))
+ return -ERANGE;
+
+ if (selected_ciphers[algo])
+ return 1;
+
+ return 0;
+}
+
+/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */
+uint32_t gprs_cipher_gen_input_ui(uint32_t iov_ui, uint8_t sapi, uint32_t lfn, uint32_t oc)
+{
+ uint32_t sx = ((1<<27) * sapi) + (1<<31);
+
+ return (iov_ui ^ sx) + lfn + oc;
+}
+
+/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */
+uint32_t gprs_cipher_gen_input_i(uint32_t iov_i, uint32_t lfn, uint32_t oc)
+{
+ return iov_i + lfn + oc;
+}
--- /dev/null
- buf[2] = char2bcd(imsi[0]) << 4 | GSM_MI_TYPE_IMSI | (odd << 3);
+/* 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-2010 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 <osmocom/core/utils.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/gsm48.h>
+
+#include <osmocom/gsm/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 */
+ },
+};
+
+/* RR elements */
+const struct tlv_definition gsm48_rr_att_tlvdef = {
+ .def = {
+ /* NOTE: Don't add IE 17 = MOBILE_ID here, it already used. */
+ [GSM48_IE_VGCS_TARGET] = { TLV_TYPE_TLV },
+ [GSM48_IE_FRQSHORT_AFTER] = { TLV_TYPE_FIXED, 9 },
+ [GSM48_IE_MUL_RATE_CFG] = { TLV_TYPE_TLV },
+ [GSM48_IE_FREQ_L_AFTER] = { TLV_TYPE_TLV },
+ [GSM48_IE_MSLOT_DESC] = { TLV_TYPE_TLV },
+ [GSM48_IE_CHANMODE_2] = { TLV_TYPE_TV },
+ [GSM48_IE_FRQSHORT_BEFORE] = { TLV_TYPE_FIXED, 9 },
+ [GSM48_IE_CHANMODE_3] = { TLV_TYPE_TV },
+ [GSM48_IE_CHANMODE_4] = { TLV_TYPE_TV },
+ [GSM48_IE_CHANMODE_5] = { TLV_TYPE_TV },
+ [GSM48_IE_CHANMODE_6] = { TLV_TYPE_TV },
+ [GSM48_IE_CHANMODE_7] = { TLV_TYPE_TV },
+ [GSM48_IE_CHANMODE_8] = { TLV_TYPE_TV },
+ [GSM48_IE_FREQ_L_BEFORE] = { TLV_TYPE_TLV },
+ [GSM48_IE_CH_DESC_1_BEFORE] = { TLV_TYPE_FIXED, 3 },
+ [GSM48_IE_CH_DESC_2_BEFORE] = { TLV_TYPE_FIXED, 3 },
+ [GSM48_IE_F_CH_SEQ_BEFORE] = { TLV_TYPE_FIXED, 9 },
+ [GSM48_IE_CLASSMARK3] = { TLV_TYPE_TLV },
+ [GSM48_IE_MA_BEFORE] = { TLV_TYPE_TLV },
+ [GSM48_IE_RR_PACKET_UL] = { TLV_TYPE_TLV },
+ [GSM48_IE_RR_PACKET_DL] = { TLV_TYPE_TLV },
+ [GSM48_IE_CELL_CH_DESC] = { TLV_TYPE_FIXED, 16 },
+ [GSM48_IE_CHANMODE_1] = { TLV_TYPE_TV },
+ [GSM48_IE_CHDES_2_AFTER] = { TLV_TYPE_FIXED, 3 },
+ [GSM48_IE_MODE_SEC_CH] = { TLV_TYPE_TV },
+ [GSM48_IE_F_CH_SEQ_AFTER] = { TLV_TYPE_FIXED, 9 },
+ [GSM48_IE_MA_AFTER] = { TLV_TYPE_TLV },
+ [GSM48_IE_BA_RANGE] = { TLV_TYPE_TLV },
+ [GSM48_IE_GROUP_CHDES] = { TLV_TYPE_TLV },
+ [GSM48_IE_BA_LIST_PREF] = { TLV_TYPE_TLV },
+ [GSM48_IE_MOB_OVSERV_DIF] = { TLV_TYPE_TLV },
+ [GSM48_IE_REALTIME_DIFF] = { TLV_TYPE_TLV },
+ [GSM48_IE_START_TIME] = { TLV_TYPE_FIXED, 2 },
+ [GSM48_IE_TIMING_ADVANCE] = { TLV_TYPE_TV },
+ [GSM48_IE_GROUP_CIP_SEQ] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_CIP_MODE_SET] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_GPRS_RESUMPT] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_SYNC_IND] = { TLV_TYPE_SINGLE_TV },
+ },
+};
+
+/* MM elements */
+const struct tlv_definition gsm48_mm_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_LOCATION_AREA] = { TLV_TYPE_FIXED, 5 },
+ [GSM48_IE_PRIORITY_LEV] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_FOLLOW_ON_PROC] = { TLV_TYPE_T },
+ [GSM48_IE_CTS_PERMISSION] = { TLV_TYPE_T },
+ },
+};
+
+static const struct value_string 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" },
+ { 0, NULL },
+};
+
+/* FIXME: convert to value_string */
+static const char *cc_state_names[32] = {
+ "NULL",
+ "INITIATED",
+ "MM_CONNECTION_PEND",
+ "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_state_name(uint8_t state)
+{
+ if (state < ARRAY_SIZE(cc_state_names))
+ return cc_state_names[state];
+
+ return "invalid";
+}
+
+static const struct value_string cc_msg_names[] = {
+ { GSM48_MT_CC_ALERTING, "ALERTING" },
+ { GSM48_MT_CC_CALL_PROC, "CALL_PROC" },
+ { GSM48_MT_CC_PROGRESS, "PROGRESS" },
+ { GSM48_MT_CC_ESTAB, "ESTAB" },
+ { GSM48_MT_CC_SETUP, "SETUP" },
+ { GSM48_MT_CC_ESTAB_CONF, "ESTAB_CONF" },
+ { GSM48_MT_CC_CONNECT, "CONNECT" },
+ { GSM48_MT_CC_CALL_CONF, "CALL_CONF" },
+ { GSM48_MT_CC_START_CC, "START_CC" },
+ { GSM48_MT_CC_RECALL, "RECALL" },
+ { GSM48_MT_CC_EMERG_SETUP, "EMERG_SETUP" },
+ { GSM48_MT_CC_CONNECT_ACK, "CONNECT_ACK" },
+ { GSM48_MT_CC_USER_INFO, "USER_INFO" },
+ { GSM48_MT_CC_MODIFY_REJECT, "MODIFY_REJECT" },
+ { GSM48_MT_CC_MODIFY, "MODIFY" },
+ { GSM48_MT_CC_HOLD, "HOLD" },
+ { GSM48_MT_CC_HOLD_ACK, "HOLD_ACK" },
+ { GSM48_MT_CC_HOLD_REJ, "HOLD_REJ" },
+ { GSM48_MT_CC_RETR, "RETR" },
+ { GSM48_MT_CC_RETR_ACK, "RETR_ACK" },
+ { GSM48_MT_CC_RETR_REJ, "RETR_REJ" },
+ { GSM48_MT_CC_MODIFY_COMPL, "MODIFY_COMPL" },
+ { GSM48_MT_CC_DISCONNECT, "DISCONNECT" },
+ { GSM48_MT_CC_RELEASE_COMPL, "RELEASE_COMPL" },
+ { GSM48_MT_CC_RELEASE, "RELEASE" },
+ { GSM48_MT_CC_STOP_DTMF, "STOP_DTMF" },
+ { GSM48_MT_CC_STOP_DTMF_ACK, "STOP_DTMF_ACK" },
+ { GSM48_MT_CC_STATUS_ENQ, "STATUS_ENQ" },
+ { GSM48_MT_CC_START_DTMF, "START_DTMF" },
+ { GSM48_MT_CC_START_DTMF_ACK, "START_DTMF_ACK" },
+ { GSM48_MT_CC_START_DTMF_REJ, "START_DTMF_REJ" },
+ { GSM48_MT_CC_CONG_CTRL, "CONG_CTRL" },
+ { GSM48_MT_CC_FACILITY, "FACILITY" },
+ { GSM48_MT_CC_STATUS, "STATUS" },
+ { GSM48_MT_CC_NOTIFY, "NOTFIY" },
+ { 0, NULL }
+};
+
+const char *gsm48_cc_msg_name(uint8_t msgtype)
+{
+ return get_value_string(cc_msg_names, msgtype);
+}
+
+const char *rr_cause_name(uint8_t cause)
+{
+ return get_value_string(rr_cause_names, cause);
+}
+
+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 (mnc > 99) {
+ 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);
+ }
+
+ 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;
- lower = char2bcd(imsi[++off]);
++ buf[2] = osmo_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;
+
- upper = char2bcd(imsi[++off]) & 0x0f;
++ lower = osmo_char2bcd(imsi[++off]);
+ if (!odd && off + 1 == length)
+ upper = 0x0f;
+ else
- *str_cur++ = bcd2char(mi[0] >> 4);
++ upper = osmo_char2bcd(imsi[++off]) & 0x0f;
+
+ buf[2 + i] = (upper << 4) | lower;
+ }
+
+ return 2 + buf[1];
+}
+
+/* Convert Mobile Identity (10.5.1.4) to string */
+int gsm48_mi_to_string(char *string, const int str_len, const uint8_t *mi,
+ const int mi_len)
+{
+ int i;
+ uint8_t mi_type;
+ char *str_cur = string;
+ uint32_t tmsi;
+
+ mi_type = mi[0] & GSM_MI_TYPE_MASK;
+
+ switch (mi_type) {
+ case GSM_MI_TYPE_NONE:
+ break;
+ case GSM_MI_TYPE_TMSI:
+ /* Table 10.5.4.3, reverse generate_mid_from_tmsi */
+ if (mi_len == GSM48_TMSI_LEN && mi[0] == (0xf0 | GSM_MI_TYPE_TMSI)) {
+ memcpy(&tmsi, &mi[1], 4);
+ tmsi = ntohl(tmsi);
+ return snprintf(string, str_len, "%u", tmsi);
+ }
+ break;
+ case GSM_MI_TYPE_IMSI:
+ case GSM_MI_TYPE_IMEI:
+ case GSM_MI_TYPE_IMEISV:
- *str_cur++ = bcd2char(mi[i] & 0xf);
++ *str_cur++ = osmo_bcd2char(mi[0] >> 4);
+
+ for (i = 1; i < mi_len; i++) {
+ if (str_cur + 2 >= string + str_len)
+ return str_cur - string;
- *str_cur++ = bcd2char(mi[i] >> 4);
++ *str_cur++ = osmo_bcd2char(mi[i] & 0xf);
+ /* skip last nibble in last input byte when GSM_EVEN */
+ if( (i != mi_len-1) || (mi[0] & GSM_MI_ODD))
++ *str_cur++ = osmo_bcd2char(mi[i] >> 4);
+ }
+ break;
+ default:
+ break;
+ }
+ *str_cur++ = '\0';
+
+ return str_cur - string;
+}
+
+void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf)
+{
+ raid->mcc = (buf[0] & 0xf) * 100;
+ raid->mcc += (buf[0] >> 4) * 10;
+ raid->mcc += (buf[1] & 0xf) * 1;
+
+ /* I wonder who came up with the stupidity of encoding the MNC
+ * differently depending on how many digits its decimal number has! */
+ if ((buf[1] >> 4) == 0xf) {
+ raid->mnc = (buf[2] & 0xf) * 10;
+ raid->mnc += (buf[2] >> 4) * 1;
+ } else {
+ raid->mnc = (buf[2] & 0xf) * 100;
+ raid->mnc += (buf[2] >> 4) * 10;
+ raid->mnc += (buf[1] >> 4) * 1;
+ }
+
+ raid->lac = ntohs(*(uint16_t *)(buf + 3));
+ raid->rac = buf[5];
+}
+
+int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid)
+{
+ uint16_t mcc = raid->mcc;
+ uint16_t mnc = raid->mnc;
+
+ buf[0] = ((mcc / 100) % 10) | (((mcc / 10) % 10) << 4);
+ buf[1] = (mcc % 10);
+
+ /* I wonder who came up with the stupidity of encoding the MNC
+ * differently depending on how many digits its decimal number has! */
+ if (mnc < 100) {
+ buf[1] |= 0xf0;
+ buf[2] = ((mnc / 10) % 10) | ((mnc % 10) << 4);
+ } else {
+ buf[1] |= (mnc % 10) << 4;
+ buf[2] = ((mnc / 100) % 10) | (((mcc / 10) % 10) << 4);
+ }
+
+ *(uint16_t *)(buf+3) = htons(raid->lac);
+
+ buf[5] = raid->rac;
+
+ return 6;
+}
--- /dev/null
- static struct bsc_fd gsmtap_bfd = { .fd = -1 };
+/* GSMTAP output for Osmocom Layer2 (will only work on the host PC) */
+/*
+ * (C) 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 "../config.h"
+
+#ifdef HAVE_SYS_SELECT_H
+
+#include <osmocom/core/gsmtap_util.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/gsmtap.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/select.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/rsl.h>
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
- static int gsmtap_fd_cb(struct bsc_fd *fd, unsigned int flags)
++static struct osmo_fd gsmtap_bfd = { .fd = -1 };
++static struct osmo_fd gsmtap_sink_bfd = { .fd = -1 };
+static LLIST_HEAD(gsmtap_txqueue);
+
+uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id)
+{
+ uint8_t ret = GSMTAP_CHANNEL_UNKNOWN;
+
+ switch (rsl_chantype) {
+ case RSL_CHAN_Bm_ACCHs:
+ ret = GSMTAP_CHANNEL_TCH_F;
+ break;
+ case RSL_CHAN_Lm_ACCHs:
+ ret = GSMTAP_CHANNEL_TCH_H;
+ break;
+ case RSL_CHAN_SDCCH4_ACCH:
+ ret = GSMTAP_CHANNEL_SDCCH4;
+ break;
+ case RSL_CHAN_SDCCH8_ACCH:
+ ret = GSMTAP_CHANNEL_SDCCH8;
+ break;
+ case RSL_CHAN_BCCH:
+ ret = GSMTAP_CHANNEL_BCCH;
+ break;
+ case RSL_CHAN_RACH:
+ ret = GSMTAP_CHANNEL_RACH;
+ break;
+ case RSL_CHAN_PCH_AGCH:
+ /* it could also be AGCH... */
+ ret = GSMTAP_CHANNEL_PCH;
+ break;
+ }
+
+ if (link_id & 0x40)
+ ret |= GSMTAP_CHANNEL_ACCH;
+
+ return ret;
+}
+
+/* receive a message from L1/L2 and put it in GSMTAP */
+struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type,
+ uint8_t ss, uint32_t fn, int8_t signal_dbm,
+ uint8_t snr, const uint8_t *data, unsigned int len)
+{
+ struct msgb *msg;
+ struct gsmtap_hdr *gh;
+ uint8_t *dst;
+
+ msg = msgb_alloc(sizeof(*gh) + len, "gsmtap_tx");
+ if (!msg)
+ return NULL;
+
+ gh = (struct gsmtap_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->version = GSMTAP_VERSION;
+ gh->hdr_len = sizeof(*gh)/4;
+ gh->type = GSMTAP_TYPE_UM;
+ gh->timeslot = ts;
+ gh->sub_slot = ss;
+ gh->arfcn = htons(arfcn);
+ gh->snr_db = snr;
+ gh->signal_dbm = signal_dbm;
+ gh->frame_number = htonl(fn);
+ gh->sub_type = chan_type;
+ gh->antenna_nr = 0;
+
+ dst = msgb_put(msg, len);
+ memcpy(dst, data, len);
+
+ return msg;
+}
+
+/* receive a message from L1/L2 and put it in GSMTAP */
+int gsmtap_sendmsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss,
+ uint32_t fn, int8_t signal_dbm, uint8_t snr,
+ const uint8_t *data, unsigned int len)
+{
+ struct msgb *msg;
+
+ /* gsmtap was never initialized, so don't try to send anything */
+ if (gsmtap_bfd.fd == -1)
+ return 0;
+
+ msg = gsmtap_makemsg(arfcn, ts, chan_type, ss, fn, signal_dbm,
+ snr, data, len);
+ if (!msg)
+ return -ENOMEM;
+
+ msgb_enqueue(&gsmtap_txqueue, msg);
+ gsmtap_bfd.when |= BSC_FD_WRITE;
+
+ return 0;
+}
+
+/* Callback from select layer if we can write to the socket */
- /* FIXME: create socket */
++static int gsmtap_fd_cb(struct osmo_fd *fd, unsigned int flags)
+{
+ struct msgb *msg;
+ int rc;
+
+ if (!(flags & BSC_FD_WRITE))
+ return 0;
+
+ msg = msgb_dequeue(&gsmtap_txqueue);
+ if (!msg) {
+ /* no more messages in the queue, disable READ cb */
+ gsmtap_bfd.when = 0;
+ return 0;
+ }
+ rc = write(gsmtap_bfd.fd, msg->data, msg->len);
+ if (rc < 0) {
+ perror("writing msgb to gsmtap fd");
+ msgb_free(msg);
+ return rc;
+ }
+ if (rc != msg->len) {
+ perror("short write to gsmtap fd");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ msgb_free(msg);
+ return 0;
+}
+
+int gsmtap_init(uint32_t dst_ip)
+{
+ int rc;
+ struct sockaddr_in sin;
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(GSMTAP_UDP_PORT);
+ sin.sin_addr.s_addr = htonl(dst_ip);
+
- gsmtap_bfd.fd = 0;
++ /* create socket */
+ rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (rc < 0) {
+ perror("creating UDP socket");
+ return rc;
+ }
+ gsmtap_bfd.fd = rc;
+ rc = connect(rc, (struct sockaddr *)&sin, sizeof(sin));
+ if (rc < 0) {
+ perror("connecting UDP socket");
+ close(gsmtap_bfd.fd);
- return bsc_register_fd(&gsmtap_bfd);
++ gsmtap_bfd.fd = -1;
+ return rc;
+ }
+
+ gsmtap_bfd.when = BSC_FD_WRITE;
+ gsmtap_bfd.cb = gsmtap_fd_cb;
+ gsmtap_bfd.data = NULL;
+
++ return osmo_fd_register(&gsmtap_bfd);
++}
++
++/* Callback from select layer if we can read from the sink socket */
++static int gsmtap_sink_fd_cb(struct osmo_fd *fd, unsigned int flags)
++{
++ int rc;
++ uint8_t buf[4096];
++
++ if (!(flags & BSC_FD_READ))
++ return 0;
++
++ rc = read(fd->fd, buf, sizeof(buf));
++ if (rc < 0) {
++ perror("reading from gsmtap sink fd");
++ return rc;
++ }
++ /* simply discard any data arriving on the socket */
++
++ return 0;
++}
++
++/* Create a local 'gsmtap sink' avoiding the UDP packets being rejected
++ * with ICMP reject messages */
++int gsmtap_sink_init(uint32_t bind_ip)
++{
++ int rc;
++ struct sockaddr_in sin;
++
++ sin.sin_family = AF_INET;
++ sin.sin_port = htons(GSMTAP_UDP_PORT);
++ sin.sin_addr.s_addr = htonl(bind_ip);
++
++ rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
++ if (rc < 0) {
++ perror("creating UDP socket");
++ return rc;
++ }
++ gsmtap_sink_bfd.fd = rc;
++ rc = bind(rc, (struct sockaddr *)&sin, sizeof(sin));
++ if (rc < 0) {
++ perror("binding UDP socket");
++ close(gsmtap_sink_bfd.fd);
++ gsmtap_sink_bfd.fd = -1;
++ return rc;
++ }
++
++ gsmtap_sink_bfd.when = BSC_FD_READ;
++ gsmtap_sink_bfd.cb = gsmtap_sink_fd_cb;
++ gsmtap_sink_bfd.data = NULL;
++
++ return osmo_fd_register(&gsmtap_sink_bfd);
++
+}
+
+#endif /* HAVE_SYS_SELECT_H */
--- /dev/null
- char col[30];
- char sub[30];
- char tim[30];
+/* Debugging/Logging support code */
+
+/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2008 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 "../config.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#include <time.h>
+#include <errno.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/vty/logging.h> /* for LOGGING_STR. */
+
+const struct log_info *osmo_log_info;
+
+static struct log_context log_context;
+static void *tall_log_ctx = NULL;
+LLIST_HEAD(osmo_log_target_list);
+
+#define LOGLEVEL_DEFS 6 /* Number of loglevels.*/
+
+static const struct value_string loglevel_strs[LOGLEVEL_DEFS+1] = {
+ { 0, "EVERYTHING" },
+ { LOGL_DEBUG, "DEBUG" },
+ { LOGL_INFO, "INFO" },
+ { LOGL_NOTICE, "NOTICE" },
+ { LOGL_ERROR, "ERROR" },
+ { LOGL_FATAL, "FATAL" },
+ { 0, NULL },
+};
+
+/* You have to keep this in sync with the structure loglevel_strs. */
+const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
+ "Log simply everything",
+ "Log debug messages and higher levels",
+ "Log informational messages and higher levels",
+ "Log noticable messages and higher levels",
+ "Log error messages and higher levels",
+ "Log only fatal messages",
+ NULL,
+};
+
+int log_parse_level(const char *lvl)
+{
+ return get_string_value(loglevel_strs, lvl);
+}
+
+const char *log_level_str(unsigned int lvl)
+{
+ return get_value_string(loglevel_strs, lvl);
+}
+
+int log_parse_category(const char *category)
+{
+ int i;
+
+ for (i = 0; i < osmo_log_info->num_cat; ++i) {
+ if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Parse the category mask.
+ * The format can be this: category1:category2:category3
+ * or category1,2:category2,3:...
+ */
+void log_parse_category_mask(struct log_target* target, const char *_mask)
+{
+ int i = 0;
+ char *mask = strdup(_mask);
+ char *category_token = NULL;
+
+ /* Disable everything to enable it afterwards */
+ for (i = 0; i < ARRAY_SIZE(target->categories); ++i)
+ target->categories[i].enabled = 0;
+
+ category_token = strtok(mask, ":");
+ do {
+ for (i = 0; i < osmo_log_info->num_cat; ++i) {
+ char* colon = strstr(category_token, ",");
+ int length = strlen(category_token);
+
+ if (colon)
+ length = colon - category_token;
+
+ if (strncasecmp(osmo_log_info->cat[i].name,
+ category_token, length) == 0) {
+ int level = 0;
+
+ if (colon)
+ level = atoi(colon+1);
+
+ target->categories[i].enabled = 1;
+ target->categories[i].loglevel = level;
+ }
+ }
+ } while ((category_token = strtok(NULL, ":")));
+
+ free(mask);
+}
+
+static const char* color(int subsys)
+{
+ if (subsys < osmo_log_info->num_cat)
+ return osmo_log_info->cat[subsys].color;
+
+ return NULL;
+}
+
+static void _output(struct log_target *target, unsigned int subsys,
+ unsigned int level, char *file, int line, int cont,
+ const char *format, va_list ap)
+{
- char final[4096];
-
- /* prepare the data */
- col[0] = '\0';
- sub[0] = '\0';
- tim[0] = '\0';
- buf[0] = '\0';
+ char buf[4096];
- snprintf(col, sizeof(col), "%s", color(subsys));
- col[sizeof(col)-1] = '\0';
++ int ret, len = 0, offset = 0, rem = sizeof(buf);
+
+ /* are we using color */
+ if (target->use_color) {
+ const char *c = color(subsys);
+ if (c) {
- vsnprintf(buf, sizeof(buf), format, ap);
- buf[sizeof(buf)-1] = '\0';
-
++ ret = snprintf(buf + offset, rem, "%s", color(subsys));
++ if (ret < 0)
++ goto err;
++ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ }
+ }
- snprintf(tim, sizeof(tim), "%s ", timestr);
- tim[sizeof(tim)-1] = '\0';
+ if (!cont) {
+ if (target->print_timestamp) {
+ char *timestr;
+ time_t tm;
+ tm = time(NULL);
+ timestr = ctime(&tm);
+ timestr[strlen(timestr)-1] = '\0';
- snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line);
- sub[sizeof(sub)-1] = '\0';
++ ret = snprintf(buf + offset, rem, "%s ", timestr);
++ if (ret < 0)
++ goto err;
++ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ }
- snprintf(final, sizeof(final), "%s%s%s%s%s", col, tim, sub, buf,
- target->use_color ? "\033[0;m" : "");
- final[sizeof(final)-1] = '\0';
- target->output(target, level, final);
++ ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
++ subsys, file, line);
++ if (ret < 0)
++ goto err;
++ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ }
++ ret = vsnprintf(buf + offset, rem, format, ap);
++ if (ret < 0)
++ goto err;
++ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+
- if (output) {
- /* FIXME: copying the va_list is an ugly
- * workaround against a bug hidden somewhere in
- * _output. If we do not copy here, the first
- * call to _output() will corrupt the va_list
- * contents, and any further _output() calls
- * with the same va_list will segfault */
- va_list bp;
- va_copy(bp, ap);
- _output(tar, subsys, level, file, line, cont, format, bp);
- va_end(bp);
- }
++ ret = snprintf(buf + offset, rem, "%s",
++ target->use_color ? "\033[0;m" : "");
++ if (ret < 0)
++ goto err;
++ OSMO_SNPRINTF_RET(ret, rem, offset, len);
++err:
++ buf[sizeof(buf)-1] = '\0';
++ target->output(target, level, buf);
+}
+
+
+static void _logp(unsigned int subsys, int level, char *file, int line,
+ int cont, const char *format, va_list ap)
+{
+ struct log_target *tar;
+
+ llist_for_each_entry(tar, &osmo_log_target_list, entry) {
+ struct log_category *category;
+ int output = 0;
+
+ category = &tar->categories[subsys];
+ /* subsystem is not supposed to be logged */
+ if (!category->enabled)
+ continue;
+
+ /* Check the global log level */
+ if (tar->loglevel != 0 && level < tar->loglevel)
+ continue;
+
+ /* Check the category log level */
+ if (tar->loglevel == 0 && category->loglevel != 0 &&
+ level < category->loglevel)
+ continue;
+
+ /* Apply filters here... if that becomes messy we will
+ * need to put filters in a list and each filter will
+ * say stop, continue, output */
+ if ((tar->filter_map & LOG_FILTER_ALL) != 0)
+ output = 1;
+ else if (osmo_log_info->filter_fn)
+ output = osmo_log_info->filter_fn(&log_context,
+ tar);
++ if (!output)
++ continue;
+
- str = talloc_zero_size(NULL, size);
++ _output(tar, subsys, level, file, line, cont, format, ap);
+ }
+}
+
+void logp(unsigned int subsys, char *file, int line, int cont,
+ const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ _logp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
+ va_end(ap);
+}
+
+void logp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ _logp(subsys, level, file, line, cont, format, ap);
+ va_end(ap);
+}
+
+void log_add_target(struct log_target *target)
+{
+ llist_add_tail(&target->entry, &osmo_log_target_list);
+}
+
+void log_del_target(struct log_target *target)
+{
+ llist_del(&target->entry);
+}
+
+void log_reset_context(void)
+{
+ memset(&log_context, 0, sizeof(log_context));
+}
+
+int log_set_context(uint8_t ctx_nr, void *value)
+{
+ if (ctx_nr > LOG_MAX_CTX)
+ return -EINVAL;
+
+ log_context.ctx[ctx_nr] = value;
+
+ return 0;
+}
+
+void log_set_all_filter(struct log_target *target, int all)
+{
+ if (all)
+ target->filter_map |= LOG_FILTER_ALL;
+ else
+ target->filter_map &= ~LOG_FILTER_ALL;
+}
+
+void log_set_use_color(struct log_target *target, int use_color)
+{
+ target->use_color = use_color;
+}
+
+void log_set_print_timestamp(struct log_target *target, int print_timestamp)
+{
+ target->print_timestamp = print_timestamp;
+}
+
+void log_set_log_level(struct log_target *target, int log_level)
+{
+ target->loglevel = log_level;
+}
+
+void log_set_category_filter(struct log_target *target, int category,
+ int enable, int level)
+{
+ if (category >= osmo_log_info->num_cat)
+ return;
+ target->categories[category].enabled = !!enable;
+ target->categories[category].loglevel = level;
+}
+
+static void _file_output(struct log_target *target, unsigned int level,
+ const char *log)
+{
+ fprintf(target->tgt_file.out, "%s", log);
+ fflush(target->tgt_file.out);
+}
+
+struct log_target *log_target_create(void)
+{
+ struct log_target *target;
+ unsigned int i;
+
+ target = talloc_zero(tall_log_ctx, struct log_target);
+ if (!target)
+ return NULL;
+
+ INIT_LLIST_HEAD(&target->entry);
+
+ /* initialize the per-category enabled/loglevel from defaults */
+ for (i = 0; i < osmo_log_info->num_cat; i++) {
+ struct log_category *cat = &target->categories[i];
+ cat->enabled = osmo_log_info->cat[i].enabled;
+ cat->loglevel = osmo_log_info->cat[i].loglevel;
+ }
+
+ /* global settings */
+ target->use_color = 1;
+ target->print_timestamp = 0;
+
+ /* global log level */
+ target->loglevel = 0;
+ return target;
+}
+
+struct log_target *log_target_create_stderr(void)
+{
+/* since C89/C99 says stderr is a macro, we can safely do this! */
+#ifdef stderr
+ struct log_target *target;
+
+ target = log_target_create();
+ if (!target)
+ return NULL;
+
+ target->type = LOG_TGT_TYPE_STDERR;
+ target->tgt_file.out = stderr;
+ target->output = _file_output;
+ return target;
+#else
+ return NULL;
+#endif /* stderr */
+}
+
+struct log_target *log_target_create_file(const char *fname)
+{
+ struct log_target *target;
+
+ target = log_target_create();
+ if (!target)
+ return NULL;
+
+ target->type = LOG_TGT_TYPE_FILE;
+ target->tgt_file.out = fopen(fname, "a");
+ if (!target->tgt_file.out)
+ return NULL;
+
+ target->output = _file_output;
+
+ target->tgt_file.fname = talloc_strdup(target, fname);
+
+ return target;
+}
+
+struct log_target *log_target_find(int type, const char *fname)
+{
+ struct log_target *tgt;
+
+ llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
+ if (tgt->type != type)
+ continue;
+ if (tgt->type == LOG_TGT_TYPE_FILE) {
+ if (!strcmp(fname, tgt->tgt_file.fname))
+ return tgt;
+ } else
+ return tgt;
+ }
+ return NULL;
+}
+
+void log_target_destroy(struct log_target *target)
+{
+
+ /* just in case, to make sure we don't have any references */
+ log_del_target(target);
+
+ if (target->output == &_file_output) {
+/* since C89/C99 says stderr is a macro, we can safely do this! */
+#ifdef stderr
+ /* don't close stderr */
+ if (target->tgt_file.out != stderr)
+#endif
+ {
+ fclose(target->tgt_file.out);
+ target->tgt_file.out = NULL;
+ }
+ }
+
+ talloc_free(target);
+}
+
+/* close and re-open a log file (for log file rotation) */
+int log_target_file_reopen(struct log_target *target)
+{
+ fclose(target->tgt_file.out);
+
+ target->tgt_file.out = fopen(target->tgt_file.fname, "a");
+ if (!target->tgt_file.out)
+ return -errno;
+
+ /* we assume target->output already to be set */
+
+ return 0;
+}
+
+/* This generates the logging command string for VTY. */
+const char *log_vty_command_string(const struct log_info *info)
+{
+ int len = 0, offset = 0, ret, i, rem;
+ int size = strlen("logging level () ()") + 1;
+ char *str;
+
+ for (i = 0; i < info->num_cat; i++)
+ size += strlen(info->cat[i].name) + 1;
+
+ for (i = 0; i < LOGLEVEL_DEFS; i++)
+ size += strlen(loglevel_strs[i].str) + 1;
+
+ rem = size;
- str = talloc_zero_size(NULL, size);
++ str = talloc_zero_size(tall_log_ctx, size);
+ if (!str)
+ return NULL;
+
+ ret = snprintf(str + offset, rem, "logging level (all|");
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+
+ for (i = 0; i < info->num_cat; i++) {
+ int j, name_len = strlen(info->cat[i].name)+1;
+ char name[name_len];
+
+ for (j = 0; j < name_len; j++)
+ name[j] = tolower(info->cat[i].name[j]);
+
+ name[name_len-1] = '\0';
+ ret = snprintf(str + offset, rem, "%s|", name+1);
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ }
+ offset--; /* to remove the trailing | */
+ rem++;
+
+ ret = snprintf(str + offset, rem, ") (");
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+
+ for (i = 0; i < LOGLEVEL_DEFS; i++) {
+ int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
+ char loglevel_str[loglevel_str_len];
+
+ for (j = 0; j < loglevel_str_len; j++)
+ loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
+
+ loglevel_str[loglevel_str_len-1] = '\0';
+ ret = snprintf(str + offset, rem, "%s|", loglevel_str);
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ }
+ offset--; /* to remove the trailing | */
+ rem++;
+
+ ret = snprintf(str + offset, rem, ")");
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+err:
++ str[size-1] = '\0';
+ return str;
+}
+
+/* This generates the logging command description for VTY. */
+const char *log_vty_command_description(const struct log_info *info)
+{
+ char *str;
+ int i, ret, len = 0, offset = 0, rem;
+ unsigned int size =
+ strlen(LOGGING_STR
+ "Set the log level for a specified category\n") + 1;
+
+ for (i = 0; i < info->num_cat; i++)
+ size += strlen(info->cat[i].description) + 1;
+
+ for (i = 0; i < LOGLEVEL_DEFS; i++)
+ size += strlen(loglevel_descriptions[i]) + 1;
+
++ size += strlen("Global setting for all subsystems") + 1;
+ rem = size;
++ str = talloc_zero_size(tall_log_ctx, size);
+ if (!str)
+ return NULL;
+
+ ret = snprintf(str + offset, rem, LOGGING_STR
+ "Set the log level for a specified category\n");
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+
++ ret = snprintf(str + offset, rem,
++ "Global setting for all subsystems\n");
++ if (ret < 0)
++ goto err;
++ OSMO_SNPRINTF_RET(ret, rem, offset, len);
++
+ for (i = 0; i < info->num_cat; i++) {
+ ret = snprintf(str + offset, rem, "%s\n",
+ info->cat[i].description);
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ }
+ for (i = 0; i < LOGLEVEL_DEFS; i++) {
+ ret = snprintf(str + offset, rem, "%s\n",
+ loglevel_descriptions[i]);
+ if (ret < 0)
+ goto err;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ }
+err:
++ str[size-1] = '\0';
+ return str;
+}
+
+void log_init(const struct log_info *cat)
+{
+ tall_log_ctx = talloc_named_const(NULL, 1, "logging");
+ osmo_log_info = cat;
+}
--- /dev/null
- static struct msg_entry *alloc_entry(struct msg_entries *entries,
- const char *mcc, const char *mnc,
- const char *option, const char *text)
+/*
+ * Parse a simple file with messages, e.g used for USSD messages
+ *
+ * (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 <osmocom/core/msgfile.h>
+#include <osmocom/core/talloc.h>
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
- struct msg_entry *entry = talloc_zero(entries, struct msg_entry);
++static struct osmo_config_entry *
++alloc_entry(struct osmo_config_list *entries,
++ const char *mcc, const char *mnc,
++ const char *option, const char *text)
+{
- static struct msg_entries *alloc_entries(void *ctx)
++ struct osmo_config_entry *entry =
++ talloc_zero(entries, struct osmo_config_entry);
+ if (!entry)
+ return NULL;
+
+ entry->mcc = talloc_strdup(entry, mcc);
+ entry->mnc = talloc_strdup(entry, mnc);
+ entry->option = talloc_strdup(entry, option);
+ entry->text = talloc_strdup(entry, text);
+
+ llist_add_tail(&entry->list, &entries->entry);
+ return entry;
+}
+
- struct msg_entries *entries;
++static struct osmo_config_list *alloc_entries(void *ctx)
+{
- entries = talloc_zero(ctx, struct msg_entries);
++ struct osmo_config_list *entries;
+
- static void handle_line(struct msg_entries *entries, char *line)
++ entries = talloc_zero(ctx, struct osmo_config_list);
+ if (!entries)
+ return NULL;
+
+ INIT_LLIST_HEAD(&entries->entry);
+ return entries;
+}
+
+/*
+ * split a line like 'foo:Text'.
+ */
- struct msg_entries *msg_entry_parse(void *ctx, const char *filename)
++static void handle_line(struct osmo_config_list *entries, char *line)
+{
+ int i;
+ const int len = strlen(line);
+
+ char *items[3];
+ int last_item = 0;
+
+ /* Skip comments from the file */
+ if (line[0] == '#')
+ return;
+
+ for (i = 0; i < len; ++i) {
+ if (line[i] == '\n' || line[i] == '\r')
+ line[i] = '\0';
+ else if (line[i] == ':' && last_item < 3) {
+ line[i] = '\0';
+
+ items[last_item++] = &line[i + 1];
+ }
+ }
+
+ if (last_item == 3) {
+ alloc_entry(entries, &line[0] , items[0], items[1], items[2]);
+ return;
+ }
+
+ /* nothing found */
+}
+
- struct msg_entries *entries;
++struct osmo_config_list *osmo_config_list_parse(void *ctx, const char *filename)
+{
++ struct osmo_config_list *entries;
+ size_t n;
+ char *line;
+ FILE *file;
+
+ file = fopen(filename, "r");
+ if (!file)
+ return NULL;
+
+ entries = alloc_entries(ctx);
+ if (!entries) {
+ fclose(file);
+ return NULL;
+ }
+
+ n = 2342;
+ line = NULL;
+ while (getline(&line, &n, file) != -1) {
+ handle_line(entries, line);
+ free(line);
+ line = NULL;
+ }
+
+ fclose(file);
+ return entries;
+}
--- /dev/null
- generate_backtrace();
+/* Panic handling */
+/*
+ * (C) 2010 by Sylvain Munaut <tnt@246tNt.com>
+ *
+ * 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 <osmocom/gsm/gsm_utils.h>
+#include <osmocom/core/panic.h>
+#include <osmocom/core/backtrace.h>
+
+#include "../config.h"
+
+
+static osmo_panic_handler_t osmo_panic_handler = (void*)0;
+
+
+#ifndef PANIC_INFLOOP
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static void osmo_panic_default(const char *fmt, va_list args)
+{
+ vfprintf(stderr, fmt, args);
++ osmo_generate_backtrace();
+ abort();
+}
+
+#else
+
+static void osmo_panic_default(const char *fmt, va_list args)
+{
+ while (1);
+}
+
+#endif
+
+
+void osmo_panic(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+
+ if (osmo_panic_handler)
+ osmo_panic_handler(fmt, args);
+ else
+ osmo_panic_default(fmt, args);
+
+ va_end(args);
+}
+
+
+void osmo_set_panic_handler(osmo_panic_handler_t h)
+{
+ osmo_panic_handler = h;
+}
+
--- /dev/null
- int plugin_load_all(const char *directory)
+/* plugin infrastructure */
+
+/* (C) 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 "../config.h"
+
+#if HAVE_DLFCN_H
+
+#include <dirent.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <osmocom/core/plugin.h>
+
- int plugin_load_all(const char *directory)
++int osmo_plugin_load_all(const char *directory)
+{
+ unsigned int num = 0;
+ char fname[PATH_MAX];
+ DIR *dir;
+ struct dirent *entry;
+
+ dir = opendir(directory);
+ if (!dir)
+ return -errno;
+
+ while ((entry = readdir(dir))) {
+ snprintf(fname, sizeof(fname), "%s/%s", directory,
+ entry->d_name);
+ if (dlopen(fname, RTLD_NOW))
+ num++;
+ }
+
+ closedir(dir);
+
+ return num;
+}
+#else
++int osmo_plugin_load_all(const char *directory)
+{
+ return 0;
+}
+#endif /* HAVE_DLFCN_H */
--- /dev/null
- static struct timer_list rate_ctr_timer;
+/* utility routines for keeping conters about events and the event rates */
+
+/* (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 <stdint.h>
+#include <string.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/rate_ctr.h>
+
+static LLIST_HEAD(rate_ctr_groups);
+
+static void *tall_rate_ctr_ctx;
+
+struct rate_ctr_group *rate_ctr_group_alloc(void *ctx,
+ const struct rate_ctr_group_desc *desc,
+ unsigned int idx)
+{
+ unsigned int size;
+ struct rate_ctr_group *group;
+
+ size = sizeof(struct rate_ctr_group) +
+ desc->num_ctr * sizeof(struct rate_ctr);
+
+ if (!ctx)
+ ctx = tall_rate_ctr_ctx;
+
+ group = talloc_zero_size(ctx, size);
+ if (!group)
+ return NULL;
+
+ group->desc = desc;
+ group->idx = idx;
+
+ llist_add(&group->list, &rate_ctr_groups);
+
+ return group;
+}
+
+void rate_ctr_group_free(struct rate_ctr_group *grp)
+{
+ llist_del(&grp->list);
+ talloc_free(grp);
+}
+
+void rate_ctr_add(struct rate_ctr *ctr, int inc)
+{
+ ctr->current += inc;
+}
+
+static void interval_expired(struct rate_ctr *ctr, enum rate_ctr_intv intv)
+{
+ /* calculate rate over last interval */
+ ctr->intv[intv].rate = ctr->current - ctr->intv[intv].last;
+ /* save current counter for next interval */
+ ctr->intv[intv].last = ctr->current;
+
+ /* update the rate of the next bigger interval. This will
+ * be overwritten when that next larger interval expires */
+ if (intv + 1 < ARRAY_SIZE(ctr->intv))
+ ctr->intv[intv+1].rate += ctr->intv[intv].rate;
+}
+
- bsc_schedule_timer(&rate_ctr_timer, 1, 0);
++static struct osmo_timer_list rate_ctr_timer;
+static uint64_t timer_ticks;
+
+/* The one-second interval has expired */
+static void rate_ctr_group_intv(struct rate_ctr_group *grp)
+{
+ unsigned int i;
+
+ for (i = 0; i < grp->desc->num_ctr; i++) {
+ struct rate_ctr *ctr = &grp->ctr[i];
+
+ interval_expired(ctr, RATE_CTR_INTV_SEC);
+ if ((timer_ticks % 60) == 0)
+ interval_expired(ctr, RATE_CTR_INTV_MIN);
+ if ((timer_ticks % (60*60)) == 0)
+ interval_expired(ctr, RATE_CTR_INTV_HOUR);
+ if ((timer_ticks % (24*60*60)) == 0)
+ interval_expired(ctr, RATE_CTR_INTV_DAY);
+ }
+}
+
+static void rate_ctr_timer_cb(void *data)
+{
+ struct rate_ctr_group *ctrg;
+
+ /* Increment number of ticks before we calculate intervals,
+ * as a counter value of 0 would already wrap all counters */
+ timer_ticks++;
+
+ llist_for_each_entry(ctrg, &rate_ctr_groups, list)
+ rate_ctr_group_intv(ctrg);
+
- bsc_schedule_timer(&rate_ctr_timer, 1, 0);
++ osmo_timer_schedule(&rate_ctr_timer, 1, 0);
+}
+
+int rate_ctr_init(void *tall_ctx)
+{
+ tall_rate_ctr_ctx = tall_ctx;
+ rate_ctr_timer.cb = rate_ctr_timer_cb;
++ osmo_timer_schedule(&rate_ctr_timer, 1, 0);
+
+ return 0;
+}
+
+struct rate_ctr_group *rate_ctr_get_group_by_name_idx(const char *name, const unsigned int idx)
+{
+ struct rate_ctr_group *ctrg;
+
+ llist_for_each_entry(ctrg, &rate_ctr_groups, list) {
+ if (!ctrg->desc)
+ continue;
+
+ if (!strcmp(ctrg->desc->group_name_prefix, name) &&
+ ctrg->idx == idx) {
+ return ctrg;
+ }
+ }
+ return NULL;
+}
+
+const struct rate_ctr *rate_ctr_get_by_name(const struct rate_ctr_group *ctrg, const char *name)
+{
+ int i;
+ const struct rate_ctr_desc *ctr_desc;
+
+ if (!ctrg->desc)
+ return NULL;
+
+ for (i = 0; i < ctrg->desc->num_ctr; i++) {
+ ctr_desc = &ctrg->desc->ctr_desc[i];
+
+ if (!strcmp(ctr_desc->name, name)) {
+ return &ctrg->ctr[i];
+ }
+ }
+ return NULL;
+}
--- /dev/null
- static LLIST_HEAD(bsc_fds);
+/* select filedescriptor handling, taken from:
+ * userspace logging daemon for the iptables ULOG target
+ * of the linux 2.4 netfilter subsystem.
+ *
+ * (C) 2000-2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/timer.h>
+
+#include "../config.h"
+
+#ifdef HAVE_SYS_SELECT_H
+
+static int maxfd = 0;
- int bsc_register_fd(struct bsc_fd *fd)
++static LLIST_HEAD(osmo_fds);
+static int unregistered_count;
+
- struct bsc_fd *entry;
- llist_for_each_entry(entry, &bsc_fds, list) {
++int osmo_fd_register(struct osmo_fd *fd)
+{
+ int flags;
+
+ /* make FD nonblocking */
+ flags = fcntl(fd->fd, F_GETFL);
+ if (flags < 0)
+ return flags;
+ flags |= O_NONBLOCK;
+ flags = fcntl(fd->fd, F_SETFL, flags);
+ if (flags < 0)
+ return flags;
+
+ /* Register FD */
+ if (fd->fd > maxfd)
+ maxfd = fd->fd;
+
+#ifdef BSC_FD_CHECK
- fprintf(stderr, "Adding a bsc_fd that is already in the list.\n");
++ struct osmo_fd *entry;
++ llist_for_each_entry(entry, &osmo_fds, list) {
+ if (entry == fd) {
- llist_add_tail(&fd->list, &bsc_fds);
++ fprintf(stderr, "Adding a osmo_fd that is already in the list.\n");
+ return 0;
+ }
+ }
+#endif
+
- void bsc_unregister_fd(struct bsc_fd *fd)
++ llist_add_tail(&fd->list, &osmo_fds);
+
+ return 0;
+}
+
- int bsc_select_main(int polling)
++void osmo_fd_unregister(struct osmo_fd *fd)
+{
+ unregistered_count++;
+ llist_del(&fd->list);
+}
+
- struct bsc_fd *ufd, *tmp;
++int osmo_select_main(int polling)
+{
- llist_for_each_entry(ufd, &bsc_fds, list) {
++ struct osmo_fd *ufd, *tmp;
+ fd_set readset, writeset, exceptset;
+ int work = 0, rc;
+ struct timeval no_time = {0, 0};
+
+ FD_ZERO(&readset);
+ FD_ZERO(&writeset);
+ FD_ZERO(&exceptset);
+
+ /* prepare read and write fdsets */
- bsc_timer_check();
++ llist_for_each_entry(ufd, &osmo_fds, list) {
+ if (ufd->when & BSC_FD_READ)
+ FD_SET(ufd->fd, &readset);
+
+ if (ufd->when & BSC_FD_WRITE)
+ FD_SET(ufd->fd, &writeset);
+
+ if (ufd->when & BSC_FD_EXCEPT)
+ FD_SET(ufd->fd, &exceptset);
+ }
+
- bsc_prepare_timers();
- rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : bsc_nearest_timer());
++ osmo_timers_check();
+
+ if (!polling)
- bsc_update_timers();
++ osmo_timers_prepare();
++ rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest());
+ if (rc < 0)
+ return 0;
+
+ /* fire timers */
- llist_for_each_entry_safe(ufd, tmp, &bsc_fds, list) {
++ osmo_timers_update();
+
+ /* call registered callback functions */
+restart:
+ unregistered_count = 0;
++ llist_for_each_entry_safe(ufd, tmp, &osmo_fds, list) {
+ int flags = 0;
+
+ if (FD_ISSET(ufd->fd, &readset)) {
+ flags |= BSC_FD_READ;
+ FD_CLR(ufd->fd, &readset);
+ }
+
+ if (FD_ISSET(ufd->fd, &writeset)) {
+ flags |= BSC_FD_WRITE;
+ FD_CLR(ufd->fd, &writeset);
+ }
+
+ if (FD_ISSET(ufd->fd, &exceptset)) {
+ flags |= BSC_FD_EXCEPT;
+ FD_CLR(ufd->fd, &exceptset);
+ }
+
+ if (flags) {
+ work = 1;
+ ufd->cb(ufd, flags);
+ }
+ /* ugly, ugly hack. If more than one filedescriptors were
+ * unregistered, they might have been consecutive and
+ * llist_for_each_entry_safe() is no longer safe */
+ /* this seems to happen with the last element of the list as well */
+ if (unregistered_count >= 1)
+ goto restart;
+ }
+ return work;
+}
+
+#endif /* _HAVE_SYS_SELECT_H */
--- /dev/null
- signal_cbfn *cbfn;
+/* Generic signalling/notification infrastructure */
+/* (C) 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 <osmocom/core/signal.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/linuxlist.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+void *tall_sigh_ctx;
+static LLIST_HEAD(signal_handler_list);
+
+struct signal_handler {
+ struct llist_head entry;
+ unsigned int subsys;
- int register_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data)
++ osmo_signal_cbfn *cbfn;
+ void *data;
+};
+
+
- void unregister_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data)
++int osmo_signal_register_handler(unsigned int subsys,
++ osmo_signal_cbfn *cbfn, void *data)
+{
+ struct signal_handler *sig_data;
+
+ sig_data = talloc(tall_sigh_ctx, struct signal_handler);
+ if (!sig_data)
+ return -ENOMEM;
+
+ memset(sig_data, 0, sizeof(*sig_data));
+
+ sig_data->subsys = subsys;
+ sig_data->data = data;
+ sig_data->cbfn = cbfn;
+
+ /* FIXME: check if we already have a handler for this subsys/cbfn/data */
+
+ llist_add_tail(&sig_data->entry, &signal_handler_list);
+
+ return 0;
+}
+
- void dispatch_signal(unsigned int subsys, unsigned int signal, void *signal_data)
++void osmo_signal_unregister_handler(unsigned int subsys,
++ osmo_signal_cbfn *cbfn, void *data)
+{
+ struct signal_handler *handler;
+
+ llist_for_each_entry(handler, &signal_handler_list, entry) {
+ if (handler->cbfn == cbfn && handler->data == data
+ && subsys == handler->subsys) {
+ llist_del(&handler->entry);
+ talloc_free(handler);
+ break;
+ }
+ }
+}
+
+
++void osmo_signal_dispatch(unsigned int subsys, unsigned int signal,
++ void *signal_data)
+{
+ struct signal_handler *handler;
+
+ llist_for_each_entry(handler, &signal_handler_list, entry) {
+ if (handler->subsys != subsys)
+ continue;
+ (*handler->cbfn)(subsys, signal, handler->data, signal_data);
+ }
+}
--- /dev/null
- struct counter *counter_alloc(const char *name)
+/* utility routines for keeping some statistics */
+
+/* (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 <string.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/statistics.h>
+
+static LLIST_HEAD(counters);
+
+void *tall_ctr_ctx;
+
- struct counter *ctr = talloc_zero(tall_ctr_ctx, struct counter);
++struct osmo_counter *osmo_counter_alloc(const char *name)
+{
- void counter_free(struct counter *ctr)
++ struct osmo_counter *ctr = talloc_zero(tall_ctr_ctx, struct osmo_counter);
+
+ if (!ctr)
+ return NULL;
+
+ ctr->name = name;
+ llist_add_tail(&ctr->list, &counters);
+
+ return ctr;
+}
+
- int counters_for_each(int (*handle_counter)(struct counter *, void *), void *data)
++void osmo_counter_free(struct osmo_counter *ctr)
+{
+ llist_del(&ctr->list);
+ talloc_free(ctr);
+}
+
- struct counter *ctr;
++int osmo_counters_for_each(int (*handle_counter)(struct osmo_counter *, void *),
++ void *data)
+{
- struct counter *counter_get_by_name(const char *name)
++ struct osmo_counter *ctr;
+ int rc = 0;
+
+ llist_for_each_entry(ctr, &counters, list) {
+ rc = handle_counter(ctr, data);
+ if (rc < 0)
+ return rc;
+ }
+
+ return rc;
+}
+
- struct counter *ctr;
++struct osmo_counter *osmo_counter_get_by_name(const char *name)
+{
++ struct osmo_counter *ctr;
+
+ llist_for_each_entry(ctr, &counters, list) {
+ if (!strcmp(ctr->name, name))
+ return ctr;
+ }
+ return NULL;
+}
--- /dev/null
- void bsc_add_timer(struct timer_list *timer)
+/*
+ * (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 <assert.h>
+#include <string.h>
+#include <osmocom/core/timer.h>
+
+static LLIST_HEAD(timer_list);
+static struct timeval s_nearest_time;
+static struct timeval s_select_time;
+
+#define MICRO_SECONDS 1000000LL
+
+#define TIME_SMALLER(left, right) \
+ (left.tv_sec*MICRO_SECONDS+left.tv_usec) <= (right.tv_sec*MICRO_SECONDS+right.tv_usec)
+
- struct timer_list *list_timer;
++void osmo_timer_add(struct osmo_timer_list *timer)
+{
- void bsc_schedule_timer(struct timer_list *timer, int seconds, int microseconds)
++ struct osmo_timer_list *list_timer;
+
+ /* TODO: Optimize and remember the closest item... */
+ timer->active = 1;
+
+ /* this might be called from within update_timers */
+ llist_for_each_entry(list_timer, &timer_list, entry)
+ if (timer == list_timer)
+ return;
+
+ timer->in_list = 1;
+ llist_add(&timer->entry, &timer_list);
+}
+
- bsc_add_timer(timer);
++void
++osmo_timer_schedule(struct osmo_timer_list *timer, int seconds, int microseconds)
+{
+ struct timeval current_time;
+
+ gettimeofday(¤t_time, NULL);
+ unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec;
+ currentTime += seconds * MICRO_SECONDS + microseconds;
+ timer->timeout.tv_sec = currentTime / MICRO_SECONDS;
+ timer->timeout.tv_usec = currentTime % MICRO_SECONDS;
- void bsc_del_timer(struct timer_list *timer)
++ osmo_timer_add(timer);
+}
+
- int bsc_timer_pending(struct timer_list *timer)
++void osmo_timer_del(struct osmo_timer_list *timer)
+{
+ if (timer->in_list) {
+ timer->active = 0;
+ timer->in_list = 0;
+ llist_del(&timer->entry);
+ }
+}
+
- struct timeval *bsc_nearest_timer()
++int osmo_timer_pending(struct osmo_timer_list *timer)
+{
+ return timer->active;
+}
+
+/*
+ * if we have a nearest time return the delta between the current
+ * time and the time of the nearest timer.
+ * If the nearest timer timed out return NULL and then we will
+ * dispatch everything after the select
+ */
- void bsc_prepare_timers()
++struct timeval *osmo_timers_nearest()
+{
+ struct timeval current_time;
+
+ if (s_nearest_time.tv_sec == 0 && s_nearest_time.tv_usec == 0)
+ return NULL;
+
+ if (gettimeofday(¤t_time, NULL) == -1)
+ return NULL;
+
+ unsigned long long nearestTime = s_nearest_time.tv_sec * MICRO_SECONDS + s_nearest_time.tv_usec;
+ unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec;
+
+ if (nearestTime < currentTime) {
+ s_select_time.tv_sec = 0;
+ s_select_time.tv_usec = 0;
+ } else {
+ s_select_time.tv_sec = (nearestTime - currentTime) / MICRO_SECONDS;
+ s_select_time.tv_usec = (nearestTime - currentTime) % MICRO_SECONDS;
+ }
+
+ return &s_select_time;
+}
+
+/*
+ * Find the nearest time and update s_nearest_time
+ */
- struct timer_list *timer, *nearest_timer = NULL;
++void osmo_timers_prepare()
+{
- int bsc_update_timers()
++ struct osmo_timer_list *timer, *nearest_timer = NULL;
+ llist_for_each_entry(timer, &timer_list, entry) {
+ if (!nearest_timer || TIME_SMALLER(timer->timeout, nearest_timer->timeout)) {
+ nearest_timer = timer;
+ }
+ }
+
+ if (nearest_timer) {
+ s_nearest_time = nearest_timer->timeout;
+ } else {
+ memset(&s_nearest_time, 0, sizeof(struct timeval));
+ }
+}
+
+/*
+ * fire all timers... and remove them
+ */
- struct timer_list *timer, *tmp;
++int osmo_timers_update()
+{
+ struct timeval current_time;
- bsc_del_timer(timer);
++ struct osmo_timer_list *timer, *tmp;
+ int work = 0;
+
+ gettimeofday(¤t_time, NULL);
+
+ /*
+ * The callbacks might mess with our list and in this case
+ * even llist_for_each_entry_safe is not safe to use. To allow
+ * del_timer, add_timer, schedule_timer to be called from within
+ * the callback we jump through some loops.
+ *
+ * First we set the handled flag of each active timer to zero,
+ * then we iterate over the list and execute the callbacks. As the
+ * list might have been changed (specially the next) from within
+ * the callback we have to start over again. Once every callback
+ * is dispatched we will remove the non-active from the list.
+ *
+ * TODO: If this is a performance issue we can poison a global
+ * variable in add_timer and del_timer and only then restart.
+ */
+ llist_for_each_entry(timer, &timer_list, entry) {
+ timer->handled = 0;
+ }
+
+restart:
+ llist_for_each_entry(timer, &timer_list, entry) {
+ if (!timer->handled && TIME_SMALLER(timer->timeout, current_time)) {
+ timer->handled = 1;
+ timer->active = 0;
+ (*timer->cb)(timer->data);
+ work = 1;
+ goto restart;
+ }
+ }
+
+ llist_for_each_entry_safe(timer, tmp, &timer_list, entry) {
+ timer->handled = 0;
+ if (!timer->active) {
- int bsc_timer_check(void)
++ osmo_timer_del(timer);
+ }
+ }
+
+ return work;
+}
+
- struct timer_list *timer;
++int osmo_timers_check(void)
+{
++ struct osmo_timer_list *timer;
+ int i = 0;
+
+ llist_for_each_entry(timer, &timer_list, entry) {
+ i++;
+ }
+ return i;
+}
--- /dev/null
- char bcd2char(uint8_t bcd)
+
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <osmocom/core/utils.h>
+
+static char namebuf[255];
+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;
+ }
+
+ snprintf(namebuf, sizeof(namebuf), "unknown 0x%x", val);
+ return namebuf;
+}
+
+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;
+}
+
- uint8_t char2bcd(char c)
++char osmo_bcd2char(uint8_t bcd)
+{
+ if (bcd < 0xa)
+ return '0' + bcd;
+ else
+ return 'A' + (bcd - 0xa);
+}
+
+/* only works for numbers in ascii */
- int hexparse(const char *str, uint8_t *b, int max_len)
++uint8_t osmo_char2bcd(char c)
+{
+ return c - 0x30;
+}
+
- static char *_hexdump(const unsigned char *buf, int len, char *delim)
++int osmo_hexparse(const char *str, uint8_t *b, int max_len)
+
+{
+ int i, l, v;
+
+ l = strlen(str);
+ if ((l&1) || ((l>>1) > max_len))
+ return -1;
+
+ memset(b, 0x00, max_len);
+
+ for (i=0; i<l; i++) {
+ char c = str[i];
+ if (c >= '0' && c <= '9')
+ v = c - '0';
+ else if (c >= 'a' && c <= 'f')
+ v = 10 + (c - 'a');
+ else if (c >= 'A' && c <= 'F')
+ v = 10 + (c - 'A');
+ else
+ return -1;
+ b[i>>1] |= v << (i&1 ? 0 : 4);
+ }
+
+ return i>>1;
+}
+
+static char hexd_buff[4096];
+
- char *ubit_dump(const uint8_t *bits, unsigned int len)
++static char *_osmo_hexdump(const unsigned char *buf, int len, char *delim)
+{
+ int i;
+ char *cur = hexd_buff;
+
+ hexd_buff[0] = 0;
+ for (i = 0; i < len; i++) {
+ int len_remain = sizeof(hexd_buff) - (cur - hexd_buff);
+ int rc = snprintf(cur, len_remain, "%02x%s", buf[i], delim);
+ if (rc <= 0)
+ break;
+ cur += rc;
+ }
+ hexd_buff[sizeof(hexd_buff)-1] = 0;
+ return hexd_buff;
+}
+
- char *hexdump(const unsigned char *buf, int len)
++char *osmo_ubit_dump(const uint8_t *bits, unsigned int len)
+{
+ int i;
+
+ if (len > sizeof(hexd_buff)-1)
+ len = sizeof(hexd_buff)-1;
+ memset(hexd_buff, 0, sizeof(hexd_buff));
+
+ for (i = 0; i < len; i++) {
+ char outch;
+ switch (bits[i]) {
+ case 0:
+ outch = '0';
+ break;
+ case 0xff:
+ outch = '?';
+ break;
+ case 1:
+ outch = '1';
+ break;
+ default:
+ outch = 'E';
+ break;
+ }
+ hexd_buff[i] = outch;
+ }
+ hexd_buff[sizeof(hexd_buff)-1] = 0;
+ return hexd_buff;
+}
+
- return _hexdump(buf, len, " ");
++char *osmo_hexdump(const unsigned char *buf, int len)
+{
- char *hexdump_nospc(const unsigned char *buf, int len)
++ return _osmo_hexdump(buf, len, " ");
+}
+
- return _hexdump(buf, len, "");
++char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len)
+{
++ return _osmo_hexdump(buf, len, "");
+}
+
+#include "../config.h"
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+void osmo_str2lower(char *out, const char *in)
+{
+ unsigned int i;
+
+ for (i = 0; i < strlen(in); i++)
+ out[i] = tolower(in[i]);
+ out[strlen(in)] = '\0';
+}
+
+void osmo_str2upper(char *out, const char *in)
+{
+ unsigned int i;
+
+ for (i = 0; i < strlen(in); i++)
+ out[i] = toupper(in[i]);
+ out[strlen(in)] = '\0';
+}
+#endif /* HAVE_CTYPE_H */
--- /dev/null
+# 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
+
+if ENABLE_VTY
+lib_LTLIBRARIES = libosmovty.la
+
+libosmovty_la_SOURCES = buffer.c command.c vty.c vector.c utils.c \
+ telnet_interface.c logging_vty.c
++libosmovty_la_LDFLAGS = -version-info $(LIBVERSION)
+libosmovty_la_LIBADD = $(top_builddir)/src/libosmocore.la
+endif
--- /dev/null
- static int telnet_new_connection(struct bsc_fd *fd, unsigned int what);
+/* minimalistic telnet/network interface it might turn into a wire interface */
+/* (C) 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 <sys/socket.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/buffer.h>
+#include <osmocom/vty/command.h>
+
+/* per connection data */
+LLIST_HEAD(active_connections);
+
+static void *tall_telnet_ctx;
+
+/* per network data */
- static struct bsc_fd server_socket = {
++static int telnet_new_connection(struct osmo_fd *fd, unsigned int what);
+
- bsc_register_fd(&server_socket);
++static struct osmo_fd server_socket = {
+ .when = BSC_FD_READ,
+ .cb = telnet_new_connection,
+ .priv_nr = 0,
+};
+
+int telnet_init(void *tall_ctx, void *priv, int port)
+{
+ struct sockaddr_in sock_addr;
+ int fd, rc, on = 1;
+
+ tall_telnet_ctx = talloc_named_const(tall_ctx, 1,
+ "telnet_connection");
+
+ fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ if (fd < 0) {
+ LOGP(0, LOGL_ERROR, "Telnet interface socket creation failed\n");
+ return fd;
+ }
+
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+ memset(&sock_addr, 0, sizeof(sock_addr));
+ sock_addr.sin_family = AF_INET;
+ sock_addr.sin_port = htons(port);
+ sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ rc = bind(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr));
+ if (rc < 0) {
+ LOGP(0, LOGL_ERROR, "Telnet interface failed to bind\n");
+ close(fd);
+ return rc;
+ }
+
+ rc = listen(fd, 0);
+ if (rc < 0) {
+ LOGP(0, LOGL_ERROR, "Telnet interface failed to listen\n");
+ close(fd);
+ return rc;
+ }
+
+ server_socket.data = priv;
+ server_socket.fd = fd;
- int telnet_close_client(struct bsc_fd *fd)
++ osmo_fd_register(&server_socket);
+
+ return 0;
+}
+
+extern struct host host;
+
+static void print_welcome(int fd)
+{
+ int ret;
+ static char *msg =
+ "Welcome to the OpenBSC Control interface\r\n";
+
+ ret = write(fd, msg, strlen(msg));
+
+ if (host.app_info->copyright)
+ ret = write(fd, host.app_info->copyright, strlen(host.app_info->copyright));
+}
+
- bsc_unregister_fd(fd);
++int telnet_close_client(struct osmo_fd *fd)
+{
+ struct telnet_connection *conn = (struct telnet_connection*)fd->data;
+
+ close(fd->fd);
- static int client_data(struct bsc_fd *fd, unsigned int what)
++ osmo_fd_unregister(fd);
+
+ if (conn->dbg) {
+ log_del_target(conn->dbg);
+ talloc_free(conn->dbg);
+ }
+
+ llist_del(&conn->entry);
+ talloc_free(conn);
+ return 0;
+}
+
- static int telnet_new_connection(struct bsc_fd *fd, unsigned int what)
++static int client_data(struct osmo_fd *fd, unsigned int what)
+{
+ struct telnet_connection *conn = fd->data;
+ int rc = 0;
+
+ if (what & BSC_FD_READ) {
+ conn->fd.when &= ~BSC_FD_READ;
+ rc = vty_read(conn->vty);
+ }
+
+ /* vty might have been closed from vithin vty_read() */
+ if (!conn->vty)
+ return rc;
+
+ if (what & BSC_FD_WRITE) {
+ rc = buffer_flush_all(conn->vty->obuf, fd->fd);
+ if (rc == BUFFER_EMPTY)
+ conn->fd.when &= ~BSC_FD_WRITE;
+ }
+
+ return rc;
+}
+
- bsc_register_fd(&connection->fd);
++static int telnet_new_connection(struct osmo_fd *fd, unsigned int what)
+{
+ struct telnet_connection *connection;
+ struct sockaddr_in sockaddr;
+ socklen_t len = sizeof(sockaddr);
+ int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len);
+
+ if (new_connection < 0) {
+ LOGP(0, LOGL_ERROR, "telnet accept failed\n");
+ return new_connection;
+ }
+
+ connection = talloc_zero(tall_telnet_ctx, struct telnet_connection);
+ connection->priv = fd->data;
+ connection->fd.data = connection;
+ connection->fd.fd = new_connection;
+ connection->fd.when = BSC_FD_READ;
+ connection->fd.cb = client_data;
- struct bsc_fd *bfd = &connection->fd;
++ osmo_fd_register(&connection->fd);
+ llist_add_tail(&connection->entry, &active_connections);
+
+ print_welcome(new_connection);
+
+ connection->vty = vty_create(new_connection, connection);
+ if (!connection->vty) {
+ LOGP(0, LOGL_ERROR, "couldn't create VTY\n");
+ close(new_connection);
+ talloc_free(connection);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* callback from VTY code */
+void vty_event(enum event event, int sock, struct vty *vty)
+{
+ struct telnet_connection *connection = vty->priv;
++ struct osmo_fd *bfd = &connection->fd;
+
+ if (vty->type != VTY_TERM)
+ return;
+
+ switch (event) {
+ case VTY_READ:
+ bfd->when |= BSC_FD_READ;
+ break;
+ case VTY_WRITE:
+ bfd->when |= BSC_FD_WRITE;
+ break;
+ case VTY_CLOSED:
+ /* vty layer is about to free() vty */
+ connection->vty = NULL;
+ telnet_close_client(bfd);
+ break;
+ default:
+ break;
+ }
+}
+
--- /dev/null
- int write_queue_bfd_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 <osmocom/core/write_queue.h>
+
- struct write_queue *queue;
++int osmo_wqueue_bfd_cb(struct osmo_fd *fd, unsigned int what)
+{
- queue = container_of(fd, struct write_queue, bfd);
++ struct osmo_wqueue *queue;
+
- void write_queue_init(struct write_queue *queue, int max_length)
++ queue = container_of(fd, struct osmo_wqueue, bfd);
+
+ if (what & BSC_FD_READ)
+ queue->read_cb(fd);
+
+ if (what & BSC_FD_EXCEPT)
+ queue->except_cb(fd);
+
+ if (what & BSC_FD_WRITE) {
+ struct msgb *msg;
+
+ fd->when &= ~BSC_FD_WRITE;
+
+ /* the queue might have been emptied */
+ if (!llist_empty(&queue->msg_queue)) {
+ --queue->current_length;
+
+ msg = msgb_dequeue(&queue->msg_queue);
+ queue->write_cb(fd, msg);
+ msgb_free(msg);
+
+ if (!llist_empty(&queue->msg_queue))
+ fd->when |= BSC_FD_WRITE;
+ }
+ }
+
+ return 0;
+}
+
- queue->bfd.cb = write_queue_bfd_cb;
++void osmo_wqueue_init(struct osmo_wqueue *queue, int max_length)
+{
+ queue->max_length = max_length;
+ queue->current_length = 0;
+ queue->read_cb = NULL;
+ queue->write_cb = NULL;
- int write_queue_enqueue(struct write_queue *queue, struct msgb *data)
++ queue->bfd.cb = osmo_wqueue_bfd_cb;
+ INIT_LLIST_HEAD(&queue->msg_queue);
+}
+
- void write_queue_clear(struct write_queue *queue)
++int osmo_wqueue_enqueue(struct osmo_wqueue *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;
+}
+
++void osmo_wqueue_clear(struct osmo_wqueue *queue)
+{
+ while (!llist_empty(&queue->msg_queue)) {
+ struct msgb *msg = msgb_dequeue(&queue->msg_queue);
+ msgb_free(msg);
+ }
+
+ queue->current_length = 0;
+ queue->bfd.when &= ~BSC_FD_WRITE;
+}
--- /dev/null
- static void dump_entries(struct msg_entries *entries)
+/*
+ * (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 <osmocom/core/msgfile.h>
+
+#include <stdio.h>
+
- struct msg_entry *entry;
++static void dump_entries(struct osmo_config_list *entries)
+{
- struct msg_entries *entries;
++ struct osmo_config_entry *entry;
+
+ if (!entries) {
+ fprintf(stderr, "Failed to parse the file\n");
+ return;
+ }
+
+ llist_for_each_entry(entry, &entries->entry, list) {
+ printf("Entry '%s:%s:%s:%s'\n",
+ entry->mcc, entry->mnc, entry->option, entry->text);
+ }
+}
+
+int main(int argc, char **argv)
+{
- entries = msg_entry_parse(NULL, "msgconfig.cfg");
++ struct osmo_config_list *entries;
+
+ /* todo use msgfile_test.c.in and replace the path */
++ entries = osmo_config_list_parse(NULL, "msgconfig.cfg");
+ dump_entries(entries);
+
+ return 0;
+}
--- /dev/null
- static struct timer_list timer_one = {
+/*
+ * (C) 2008 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 <stdio.h>
+
+#include <osmocom/core/timer.h>
+#include <osmocom/core/select.h>
+
+#include "../../config.h"
+
+static void timer_fired(void *data);
+
- static struct timer_list timer_two = {
++static struct osmo_timer_list timer_one = {
+ .cb = timer_fired,
+ .data = (void*)1,
+};
+
- static struct timer_list timer_three = {
++static struct osmo_timer_list timer_two = {
+ .cb = timer_fired,
+ .data = (void*)2,
+};
+
- bsc_schedule_timer(&timer_one, 3, 0);
- bsc_del_timer(&timer_two);
++static struct osmo_timer_list timer_three = {
+ .cb = timer_fired,
+ .data = (void*)3,
+};
+
+static void timer_fired(void *_data)
+{
+ unsigned long data = (unsigned long) _data;
+ printf("Fired timer: %lu\n", data);
+
+ if (data == 1) {
- bsc_schedule_timer(&timer_one, 3, 0);
- bsc_schedule_timer(&timer_two, 5, 0);
- bsc_schedule_timer(&timer_three, 4, 0);
++ osmo_timer_schedule(&timer_one, 3, 0);
++ osmo_timer_del(&timer_two);
+ } else if (data == 2) {
+ printf("Should not be fired... bug in del_timer\n");
+ } else if (data == 3) {
+ printf("Timer fired not registering again\n");
+ } else {
+ printf("wtf... wrong data\n");
+ }
+}
+
+int main(int argc, char** argv)
+{
+ printf("Starting... timer\n");
+
- bsc_select_main(0);
++ osmo_timer_schedule(&timer_one, 3, 0);
++ osmo_timer_schedule(&timer_two, 5, 0);
++ osmo_timer_schedule(&timer_three, 4, 0);
+
+#ifdef HAVE_SYS_SELECT_H
+ while (1) {
++ osmo_select_main(0);
+ }
+#else
+ printf("Select not supported on this platform!\n");
+#endif
+}