summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore23
-rw-r--r--include/osmocore/bitvec.h65
-rw-r--r--include/osmocore/comp128.h22
-rw-r--r--include/osmocore/gsm_utils.h59
-rw-r--r--include/osmocore/gsmtap.h62
-rw-r--r--include/osmocore/linuxlist.h360
-rw-r--r--include/osmocore/msgb.h114
-rw-r--r--include/osmocore/protocol/gsm_04_08.h743
-rw-r--r--include/osmocore/protocol/gsm_04_11.h188
-rw-r--r--include/osmocore/protocol/gsm_04_80.h126
-rw-r--r--include/osmocore/protocol/gsm_08_58.h512
-rw-r--r--include/osmocore/protocol/gsm_12_21.h691
-rw-r--r--include/osmocore/select.h22
-rw-r--r--include/osmocore/signal.h15
-rw-r--r--include/osmocore/statistics.h31
-rw-r--r--include/osmocore/talloc.h192
-rw-r--r--include/osmocore/timer.h72
-rw-r--r--include/osmocore/tlv.h237
-rw-r--r--include/osmocore/utils.h6
-rw-r--r--src/bitvec.c170
-rw-r--r--src/comp128.c230
-rw-r--r--src/gsm_utils.c215
-rw-r--r--src/msgb.c98
-rw-r--r--src/select.c128
-rw-r--r--src/signal.c84
-rw-r--r--src/statistics.c66
-rw-r--r--src/talloc.c1805
-rw-r--r--src/timer.c185
-rw-r--r--src/tlv_parser.c171
-rw-r--r--tests/sms/sms_test.c47
-rw-r--r--tests/timer/timer_test.c76
31 files changed, 6815 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..4adf7924
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,23 @@
+Makefile
+.deps
+.libs
+*.o
+*.lo
+*.la
+*.in
+*.am
+*.pc
+aclocal.m4
+autom4te.cache
+config.h*
+config.sub
+config.log
+config.status
+config.guess
+configure
+depcomp
+missing
+ltmain.sh
+install-sh
+stamp-h1
+libtool
diff --git a/include/osmocore/bitvec.h b/include/osmocore/bitvec.h
new file mode 100644
index 00000000..11cb01ea
--- /dev/null
+++ b/include/osmocore/bitvec.h
@@ -0,0 +1,65 @@
+#ifndef _BITVEC_H
+#define _BITVEC_H
+
+/* bit vector utility routines */
+
+/* (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.
+ *
+ */
+
+
+/* In GSM mac blocks, every bit can be 0 or 1, or L or H. L/H are
+ * defined relative to the 0x2b padding pattern */
+enum bit_value {
+ ZERO = 0,
+ ONE = 1,
+ L = 2,
+ H = 3,
+};
+
+struct bitvec {
+ unsigned int cur_bit; /* curser to the next unused bit */
+ unsigned int data_len; /* length of data array in bytes */
+ uint8_t *data; /* pointer to data array */
+};
+
+/* check if the bit is 0 or 1 for a given position inside a bitvec */
+enum bit_value bitvec_get_bit_pos(struct bitvec *bv, unsigned int bitnr);
+
+/* get the Nth set bit inside the bit vector */
+unsigned int bitvec_get_nth_set_bit(struct bitvec *bv, unsigned int n);
+
+/* Set a bit at given position */
+int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnum,
+ enum bit_value bit);
+
+/* Set the next bit in the vector */
+int bitvec_set_bit(struct bitvec *bv, enum bit_value bit);
+
+/* Set multiple bits at the current position */
+int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count);
+
+/* Add an unsigned integer (of length count bits) to current position */
+int bitvec_set_uint(struct bitvec *bv, unsigned int in, int count);
+
+
+/* Pad the bit vector up to a certain bit position */
+int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit);
+
+#endif /* _BITVEC_H */
diff --git a/include/osmocore/comp128.h b/include/osmocore/comp128.h
new file mode 100644
index 00000000..c37808f0
--- /dev/null
+++ b/include/osmocore/comp128.h
@@ -0,0 +1,22 @@
+/*
+ * COMP128 header
+ *
+ * See comp128.c for details
+ */
+
+#ifndef __COMP128_H__
+#define __COMP128_H__
+
+#include <stdint.h>
+
+/*
+ * Performs the COMP128 algorithm (used as A3/A8)
+ * ki : uint8_t [16]
+ * srand : uint8_t [16]
+ * sres : uint8_t [4]
+ * kc : uint8_t [8]
+ */
+void comp128(uint8_t *ki, uint8_t *srand, uint8_t *sres, uint8_t *kc);
+
+#endif /* __COMP128_H__ */
+
diff --git a/include/osmocore/gsm_utils.h b/include/osmocore/gsm_utils.h
new file mode 100644
index 00000000..57521ac7
--- /dev/null
+++ b/include/osmocore/gsm_utils.h
@@ -0,0 +1,59 @@
+/* GSM utility functions, e.g. coding and decoding */
+/*
+ * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef GSM_UTILS_H
+#define GSM_UTILS_H
+
+#include <stdint.h>
+
+enum gsm_band {
+ GSM_BAND_850 = 1,
+ GSM_BAND_900 = 2,
+ GSM_BAND_1800 = 4,
+ GSM_BAND_1900 = 8,
+ GSM_BAND_450 = 0x10,
+ GSM_BAND_480 = 0x20,
+ GSM_BAND_750 = 0x40,
+ GSM_BAND_810 = 0x80,
+};
+
+int gsm_7bit_decode(char *decoded, const uint8_t *user_data, uint8_t length);
+int gsm_7bit_encode(uint8_t *result, const char *data);
+
+int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm);
+int ms_pwr_dbm(enum gsm_band band, uint8_t lvl);
+
+/* According to TS 08.05 Chapter 8.1.4 */
+int rxlev2dbm(uint8_t rxlev);
+uint8_t dbm2rxlev(int dbm);
+
+/* According to GSM 04.08 Chapter 10.5.2.29 */
+static inline int rach_max_trans_val2raw(int val) { return (val >> 1) & 3; }
+static inline int rach_max_trans_raw2val(int raw) {
+ const int tbl[4] = { 1, 2, 4, 7 };
+ return tbl[raw & 3];
+}
+
+void generate_backtrace();
+#endif
diff --git a/include/osmocore/gsmtap.h b/include/osmocore/gsmtap.h
new file mode 100644
index 00000000..81b9cc06
--- /dev/null
+++ b/include/osmocore/gsmtap.h
@@ -0,0 +1,62 @@
+#ifndef _GSMTAP_H
+#define _GSMTAP_H
+
+/* gsmtap header, pseudo-header in front of the actua GSM payload*/
+
+#include <stdint.h>
+
+#define GSMTAP_VERSION 0x01
+
+#define GSMTAP_TYPE_UM 0x01
+#define GSMTAP_TYPE_ABIS 0x02
+#define GSMTAP_TYPE_UM_BURST 0x03 /* raw burst bits */
+
+#define GSMTAP_BURST_UNKNOWN 0x00
+#define GSMTAP_BURST_FCCH 0x01
+#define GSMTAP_BURST_PARTIAL_SCH 0x02
+#define GSMTAP_BURST_SCH 0x03
+#define GSMTAP_BURST_CTS_SCH 0x04
+#define GSMTAP_BURST_COMPACT_SCH 0x05
+#define GSMTAP_BURST_NORMAL 0x06
+#define GSMTAP_BURST_DUMMY 0x07
+#define GSMTAP_BURST_ACCESS 0x08
+#define GSMTAP_BURST_NONE 0x09
+
+#define GSMTAP_UDP_PORT 4729
+
+struct gsmtap_hdr {
+ uint8_t version; /* version, set to 0x01 currently */
+ uint8_t hdr_len; /* length in number of 32bit words */
+ uint8_t type; /* see GSMTAP_TYPE_* */
+ uint8_t timeslot; /* timeslot (0..7 on Um) */
+
+ uint16_t arfcn; /* ARFCN (frequency) */
+ uint8_t noise_db; /* noise figure in dB */
+ uint8_t signal_db; /* signal level in dB */
+
+ uint32_t frame_number; /* GSM Frame Number (FN) */
+
+ uint8_t burst_type; /* Type of burst, see above */
+ uint8_t antenna_nr; /* Antenna Number */
+ uint16_t res; /* reserved for future use (RFU) */
+
+} __attribute__((packed));
+
+
+/* PCAP related definitions */
+#define TCPDUMP_MAGIC 0xa1b2c3d4
+#ifndef LINKTYPE_GSMTAP
+#define LINKTYPE_GSMTAP 2342
+#endif
+struct pcap_timeval {
+ int32_t tv_sec;
+ int32_t tv_usec;
+};
+
+struct pcap_sf_pkthdr {
+ struct pcap_timeval ts; /* time stamp */
+ uint32_t caplen; /* lenght of portion present */
+ uint32_t len; /* length of this packet */
+};
+
+#endif /* _GSMTAP_H */
diff --git a/include/osmocore/linuxlist.h b/include/osmocore/linuxlist.h
new file mode 100644
index 00000000..fb99c5ec
--- /dev/null
+++ b/include/osmocore/linuxlist.h
@@ -0,0 +1,360 @@
+#ifndef _LINUX_LLIST_H
+#define _LINUX_LLIST_H
+
+#include <stddef.h>
+
+#ifndef inline
+#define inline __inline__
+#endif
+
+static inline void prefetch(const void *x) {;}
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (typeof( ((type *)0)->member ) *)(ptr); \
+ (type *)( (char *)__mptr - offsetof(type, member) );})
+
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized llist entries.
+ */
+#define LLIST_POISON1 ((void *) 0x00100100)
+#define LLIST_POISON2 ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked llist implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole llists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct llist_head {
+ struct llist_head *next, *prev;
+};
+
+#define LLIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LLIST_HEAD(name) \
+ struct llist_head name = LLIST_HEAD_INIT(name)
+
+#define INIT_LLIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal llist manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __llist_add(struct llist_head *_new,
+ struct llist_head *prev,
+ struct llist_head *next)
+{
+ next->prev = _new;
+ _new->next = next;
+ _new->prev = prev;
+ prev->next = _new;
+}
+
+/**
+ * llist_add - add a new entry
+ * @new: new entry to be added
+ * @head: llist head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void llist_add(struct llist_head *_new, struct llist_head *head)
+{
+ __llist_add(_new, head, head->next);
+}
+
+/**
+ * llist_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: llist head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void llist_add_tail(struct llist_head *_new, struct llist_head *head)
+{
+ __llist_add(_new, head->prev, head);
+}
+
+/*
+ * Delete a llist entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal llist manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __llist_del(struct llist_head * prev, struct llist_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * llist_del - deletes entry from llist.
+ * @entry: the element to delete from the llist.
+ * Note: llist_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void llist_del(struct llist_head *entry)
+{
+ __llist_del(entry->prev, entry->next);
+ entry->next = (struct llist_head *)LLIST_POISON1;
+ entry->prev = (struct llist_head *)LLIST_POISON2;
+}
+
+/**
+ * llist_del_init - deletes entry from llist and reinitialize it.
+ * @entry: the element to delete from the llist.
+ */
+static inline void llist_del_init(struct llist_head *entry)
+{
+ __llist_del(entry->prev, entry->next);
+ INIT_LLIST_HEAD(entry);
+}
+
+/**
+ * llist_move - delete from one llist and add as another's head
+ * @llist: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void llist_move(struct llist_head *llist, struct llist_head *head)
+{
+ __llist_del(llist->prev, llist->next);
+ llist_add(llist, head);
+}
+
+/**
+ * llist_move_tail - delete from one llist and add as another's tail
+ * @llist: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void llist_move_tail(struct llist_head *llist,
+ struct llist_head *head)
+{
+ __llist_del(llist->prev, llist->next);
+ llist_add_tail(llist, head);
+}
+
+/**
+ * llist_empty - tests whether a llist is empty
+ * @head: the llist to test.
+ */
+static inline int llist_empty(const struct llist_head *head)
+{
+ return head->next == head;
+}
+
+static inline void __llist_splice(struct llist_head *llist,
+ struct llist_head *head)
+{
+ struct llist_head *first = llist->next;
+ struct llist_head *last = llist->prev;
+ struct llist_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+}
+
+/**
+ * llist_splice - join two llists
+ * @llist: the new llist to add.
+ * @head: the place to add it in the first llist.
+ */
+static inline void llist_splice(struct llist_head *llist, struct llist_head *head)
+{
+ if (!llist_empty(llist))
+ __llist_splice(llist, head);
+}
+
+/**
+ * llist_splice_init - join two llists and reinitialise the emptied llist.
+ * @llist: the new llist to add.
+ * @head: the place to add it in the first llist.
+ *
+ * The llist at @llist is reinitialised
+ */
+static inline void llist_splice_init(struct llist_head *llist,
+ struct llist_head *head)
+{
+ if (!llist_empty(llist)) {
+ __llist_splice(llist, head);
+ INIT_LLIST_HEAD(llist);
+ }
+}
+
+/**
+ * llist_entry - get the struct for this entry
+ * @ptr: the &struct llist_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * llist_for_each - iterate over a llist
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ */
+#define llist_for_each(pos, head) \
+ for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+ pos = pos->next, prefetch(pos->next))
+
+/**
+ * __llist_for_each - iterate over a llist
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ *
+ * This variant differs from llist_for_each() in that it's the
+ * simplest possible llist iteration code, no prefetching is done.
+ * Use this for code that knows the llist to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __llist_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * llist_for_each_prev - iterate over a llist backwards
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ */
+#define llist_for_each_prev(pos, head) \
+ for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
+ pos = pos->prev, prefetch(pos->prev))
+
+/**
+ * llist_for_each_safe - iterate over a llist safe against removal of llist entry
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @n: another &struct llist_head to use as temporary storage
+ * @head: the head for your llist.
+ */
+#define llist_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * llist_for_each_entry - iterate over llist of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry(pos, head, member) \
+ for (pos = llist_entry((head)->next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next))
+
+/**
+ * llist_for_each_entry_reverse - iterate backwards over llist of given type.
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_reverse(pos, head, member) \
+ for (pos = llist_entry((head)->prev, typeof(*pos), member), \
+ prefetch(pos->member.prev); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.prev, typeof(*pos), member), \
+ prefetch(pos->member.prev))
+
+/**
+ * llist_for_each_entry_continue - iterate over llist of given type
+ * continuing after existing point
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_continue(pos, head, member) \
+ for (pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next))
+
+/**
+ * llist_for_each_entry_safe - iterate over llist of given type safe against removal of llist entry
+ * @pos: the type * to use as a loop counter.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_safe(pos, n, head, member) \
+ for (pos = llist_entry((head)->next, typeof(*pos), member), \
+ n = llist_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = llist_entry(n->member.next, typeof(*n), member))
+
+/**
+ * llist_for_each_rcu - iterate over an rcu-protected llist
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ */
+#define llist_for_each_rcu(pos, head) \
+ for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+ pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
+
+#define __llist_for_each_rcu(pos, head) \
+ for (pos = (head)->next; pos != (head); \
+ pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
+
+/**
+ * llist_for_each_safe_rcu - iterate over an rcu-protected llist safe
+ * against removal of llist entry
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @n: another &struct llist_head to use as temporary storage
+ * @head: the head for your llist.
+ */
+#define llist_for_each_safe_rcu(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
+
+/**
+ * llist_for_each_entry_rcu - iterate over rcu llist of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_rcu(pos, head, member) \
+ for (pos = llist_entry((head)->next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ ({ smp_read_barrier_depends(); 0;}), \
+ prefetch(pos->member.next))
+
+
+/**
+ * llist_for_each_continue_rcu - iterate over an rcu-protected llist
+ * continuing after existing point.
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ */
+#define llist_for_each_continue_rcu(pos, head) \
+ for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
+ (pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
+
+
+#endif
diff --git a/include/osmocore/msgb.h b/include/osmocore/msgb.h
new file mode 100644
index 00000000..131f920a
--- /dev/null
+++ b/include/osmocore/msgb.h
@@ -0,0 +1,114 @@
+#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 "linuxlist.h"
+
+struct bts_link;
+
+struct msgb {
+ struct llist_head list;
+
+ /* ptr to the physical E1 link to the BTS(s) */
+ struct gsm_bts_link *bts_link;
+
+ /* Part of which TRX logical channel we were received / transmitted */
+ struct gsm_bts_trx *trx;
+ struct gsm_lchan *lchan;
+
+ unsigned char *l2h;
+ unsigned char *l3h;
+ unsigned char *smsh;
+
+ 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);
+
+#define msgb_l2(m) ((void *)(m->l2h))
+#define msgb_l3(m) ((void *)(m->l3h))
+#define msgb_sms(m) ((void *)(m->smsh))
+
+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 unsigned char *msgb_put(struct msgb *msgb, unsigned int len)
+{
+ unsigned char *tmp = msgb->tail;
+ msgb->tail += len;
+ msgb->len += len;
+ return tmp;
+}
+static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int 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;
+}
+static inline int msgb_tailroom(const struct msgb *msgb)
+{
+ return (msgb->data + msgb->data_len) - msgb->tail;
+}
+
+/* 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)
+{
+ struct msgb *msg = msgb_alloc(size, name);
+ if (msg)
+ msgb_reserve(msg, headroom);
+ return msg;
+}
+
+#endif /* _MSGB_H */
diff --git a/include/osmocore/protocol/gsm_04_08.h b/include/osmocore/protocol/gsm_04_08.h
new file mode 100644
index 00000000..801b9b54
--- /dev/null
+++ b/include/osmocore/protocol/gsm_04_08.h
@@ -0,0 +1,743 @@
+#ifndef PROTO_GSM_04_08_H
+#define PROTO_GSM_04_08_H
+
+#include <stdint.h>
+
+/* GSM TS 04.08 definitions */
+struct gsm_lchan;
+
+struct gsm48_classmark1 {
+ uint8_t spare:1,
+ rev_level:2,
+ es_ind:1,
+ a5_1:1,
+ pwr_lev:3;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.5 */
+struct gsm48_chan_desc {
+ uint8_t chan_nr;
+ union {
+ struct {
+ uint8_t maio_high:4,
+ h:1,
+ tsc:3;
+ uint8_t hsn:6,
+ maio_low:2;
+ } h1;
+ struct {
+ uint8_t arfcn_high:2,
+ spare:2,
+ h:1,
+ tsc:3;
+ uint8_t arfcn_low;
+ } h0;
+ };
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.21aa */
+struct gsm48_multi_rate_conf {
+ uint8_t smod : 2,
+ spare: 1,
+ icmi : 1,
+ nscb : 1,
+ ver : 3;
+ uint8_t m4_75 : 1,
+ m5_15 : 1,
+ m5_90 : 1,
+ m6_70 : 1,
+ m7_40 : 1,
+ m7_95 : 1,
+ m10_2 : 1,
+ m12_2 : 1;
+} __attribute__((packed));
+
+/* Chapter 10.5.2.30 */
+struct gsm48_req_ref {
+ uint8_t ra;
+ uint8_t t3_high:3,
+ t1_:5;
+ uint8_t t2:5,
+ t3_low:3;
+} __attribute__ ((packed));
+
+/*
+ * Chapter 9.1.5/9.1.6
+ *
+ * For 9.1.6 the chan_desc has the meaning of 10.5.2.5a
+ */
+struct gsm48_chan_mode_modify {
+ struct gsm48_chan_desc chan_desc;
+ uint8_t mode;
+} __attribute__ ((packed));
+
+enum gsm48_chan_mode {
+ GSM48_CMODE_SIGN = 0x00,
+ GSM48_CMODE_SPEECH_V1 = 0x01,
+ GSM48_CMODE_SPEECH_EFR = 0x21,
+ GSM48_CMODE_SPEECH_AMR = 0x41,
+ GSM48_CMODE_DATA_14k5 = 0x0f,
+ GSM48_CMODE_DATA_12k0 = 0x03,
+ GSM48_CMODE_DATA_6k0 = 0x0b,
+ GSM48_CMODE_DATA_3k6 = 0x23,
+};
+
+/* Chapter 9.1.2 */
+struct gsm48_ass_cmd {
+ /* Semantic is from 10.5.2.5a */
+ struct gsm48_chan_desc chan_desc;
+ uint8_t power_command;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Chapter 10.5.2.2 */
+struct gsm48_cell_desc {
+ uint8_t bcc:3,
+ ncc:3,
+ arfcn_hi:2;
+ uint8_t arfcn_lo;
+} __attribute__((packed));
+
+/* Chapter 9.1.15 */
+struct gsm48_ho_cmd {
+ struct gsm48_cell_desc cell_desc;
+ struct gsm48_chan_desc chan_desc;
+ uint8_t ho_ref;
+ uint8_t power_command;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Chapter 9.1.18 */
+struct gsm48_imm_ass {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t page_mode;
+ struct gsm48_chan_desc chan_desc;
+ struct gsm48_req_ref req_ref;
+ uint8_t timing_advance;
+ uint8_t mob_alloc_len;
+ uint8_t mob_alloc[0];
+} __attribute__ ((packed));
+
+/* Chapter 10.5.1.3 */
+struct gsm48_loc_area_id {
+ uint8_t digits[3]; /* BCD! */
+ uint16_t lac;
+} __attribute__ ((packed));
+
+/* Section 9.2.2 */
+struct gsm48_auth_req {
+ uint8_t key_seq:4,
+ spare:4;
+ uint8_t rand[16];
+} __attribute__ ((packed));
+
+/* Section 9.2.15 */
+struct gsm48_loc_upd_req {
+ uint8_t type:4,
+ key_seq:4;
+ struct gsm48_loc_area_id lai;
+ struct gsm48_classmark1 classmark1;
+ uint8_t mi_len;
+ uint8_t mi[0];
+} __attribute__ ((packed));
+
+/* Section 10.1 */
+struct gsm48_hdr {
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.3x System information Type header */
+struct gsm48_system_information_type_header {
+ uint8_t l2_plen;
+ uint8_t rr_protocol_discriminator :4,
+ skip_indicator:4;
+ uint8_t system_information;
+} __attribute__ ((packed));
+
+struct gsm48_rach_control {
+ uint8_t re :1,
+ cell_bar :1,
+ tx_integer :4,
+ max_trans :2;
+ uint8_t t2;
+ uint8_t t3;
+} __attribute__ ((packed));
+
+/* Section 10.5.2.4 Cell Selection Parameters */
+struct gsm48_cell_sel_par {
+ uint8_t ms_txpwr_max_ccch:5, /* GSM 05.08 MS-TXPWR-MAX-CCCH */
+ cell_resel_hyst:3; /* GSM 05.08 CELL-RESELECT-HYSTERESIS */
+ uint8_t rxlev_acc_min:6, /* GSM 05.08 RXLEV-ACCESS-MIN */
+ neci:1,
+ acs:1;
+} __attribute__ ((packed));
+
+/* Section 10.5.2.11 Control Channel Description , Figure 10.5.33 */
+struct gsm48_control_channel_descr {
+ uint8_t ccch_conf :3,
+ bs_ag_blks_res :3,
+ att :1,
+ spare1 :1;
+ uint8_t bs_pa_mfrms : 3,
+ spare2 :5;
+ uint8_t t3212;
+} __attribute__ ((packed));
+
+struct gsm48_cell_options {
+ uint8_t radio_link_timeout:4,
+ dtx:2,