summaryrefslogtreecommitdiff
path: root/gsmstack
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2008-12-01 01:19:19 +0530
committerHarald Welte <laforge@gnumonks.org>2008-12-01 01:19:19 +0530
commitfd405f799425b6832a0c9cc7a56b07f43288b8b1 (patch)
treec02cdfa014a63339950a98ef7e3bdbebb44adbf5 /gsmstack
parent753c2fc0f6861f6a3dcf6e5520343d93b5ed2317 (diff)
gsmstack: add new unfinished GSM burst/channel demultiplex
This is some unfinished work on a new codebase for 'proper' demultiplex of all the various physical and logical channel types. Basically this includes everything needed to get from the differential-decoded bursts up to the MAC blocks that can be passed up to layer 2.
Diffstat (limited to 'gsmstack')
-rw-r--r--gsmstack/cch.c483
-rw-r--r--gsmstack/channel.c143
-rw-r--r--gsmstack/get_lctype.c105
-rw-r--r--gsmstack/get_lctype.h7
-rw-r--r--gsmstack/gsmstack.c31
-rw-r--r--gsmstack/gsmstack.h133
-rw-r--r--gsmstack/gsmstack_chan.h12
-rw-r--r--gsmstack/gsmtap.h57
-rw-r--r--gsmstack/source_pcap_burst.c148
9 files changed, 1119 insertions, 0 deletions
diff --git a/gsmstack/cch.c b/gsmstack/cch.c
new file mode 100644
index 0000000..7a1fd66
--- /dev/null
+++ b/gsmstack/cch.c
@@ -0,0 +1,483 @@
+
+/* This file was taken from gsm-tvoid */
+
+#include "system.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "burst_types.h"
+#include "cch.h"
+#include "fire_crc.h"
+
+/*
+ * GSM SACCH -- Slow Associated Control Channel
+ *
+ * These messages are encoded exactly the same as on the BCCH.
+ * (Broadcast Control Channel.)
+ *
+ * Input: 184 bits
+ *
+ * 1. Add parity and flushing bits. (Output 184 + 40 + 4 = 228 bit)
+ * 2. Convolutional encode. (Output 228 * 2 = 456 bit)
+ * 3. Interleave. (Output 456 bit)
+ * 4. Map on bursts. (4 x 156 bit bursts with each 2x57 bit content data)
+ */
+
+
+/*
+ * Parity (FIRE) for the GSM SACCH channel.
+ *
+ * g(x) = (x^23 + 1)(x^17 + x^3 + 1)
+ * = x^40 + x^26 + x^23 + x^17 + x^3 + 1
+ */
+
+static const unsigned char parity_polynomial[PARITY_SIZE + 1] = {
+ 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 0,
+ 0, 1, 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0,
+ 1
+};
+
+// remainder after dividing data polynomial by g(x)
+static const unsigned char parity_remainder[PARITY_SIZE] = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1
+};
+
+
+/*
+static void parity_encode(unsigned char *d, unsigned char *p) {
+
+ int i;
+ unsigned char buf[DATA_BLOCK_SIZE + PARITY_SIZE], *q;
+
+ memcpy(buf, d, DATA_BLOCK_SIZE);
+ memset(buf + DATA_BLOCK_SIZE, 0, PARITY_SIZE);
+
+ for(q = buf; q < buf + DATA_BLOCK_SIZE; q++)
+ if(*q)
+ for(i = 0; i < PARITY_SIZE + 1; i++)
+ q[i] ^= parity_polynomial[i];
+ for(i = 0; i < PARITY_SIZE; i++)
+ p[i] = !buf[DATA_BLOCK_SIZE + i];
+}
+ */
+
+
+static int parity_check(unsigned char *d) {
+
+ unsigned int i;
+ unsigned char buf[DATA_BLOCK_SIZE + PARITY_SIZE], *q;
+
+ memcpy(buf, d, DATA_BLOCK_SIZE + PARITY_SIZE);
+
+ for(q = buf; q < buf + DATA_BLOCK_SIZE; q++)
+ if(*q)
+ for(i = 0; i < PARITY_SIZE + 1; i++)
+ q[i] ^= parity_polynomial[i];
+ return memcmp(buf + DATA_BLOCK_SIZE, parity_remainder, PARITY_SIZE);
+}
+
+
+/*
+ * Convolutional encoding and Viterbi decoding for the GSM SACCH channel.
+ */
+
+/*
+ * Convolutional encoding:
+ *
+ * G_0 = 1 + x^3 + x^4
+ * G_1 = 1 + x + x^3 + x^4
+ *
+ * i.e.,
+ *
+ * c_{2k} = u_k + u_{k - 3} + u_{k - 4}
+ * c_{2k + 1} = u_k + u_{k - 1} + u_{k - 3} + u_{k - 4}
+ */
+#define K 5
+#define MAX_ERROR (2 * CONV_INPUT_SIZE + 1)
+
+
+/*
+ * Given the current state and input bit, what are the output bits?
+ *
+ * encode[current_state][input_bit]
+ */
+static const unsigned int encode[1 << (K - 1)][2] = {
+ {0, 3}, {3, 0}, {3, 0}, {0, 3},
+ {0, 3}, {3, 0}, {3, 0}, {0, 3},
+ {1, 2}, {2, 1}, {2, 1}, {1, 2},
+ {1, 2}, {2, 1}, {2, 1}, {1, 2}
+};
+
+
+/*
+ * Given the current state and input bit, what is the next state?
+ *
+ * next_state[current_state][input_bit]
+ */
+static const unsigned int next_state[1 << (K - 1)][2] = {
+ {0, 8}, {0, 8}, {1, 9}, {1, 9},
+ {2, 10}, {2, 10}, {3, 11}, {3, 11},
+ {4, 12}, {4, 12}, {5, 13}, {5, 13},
+ {6, 14}, {6, 14}, {7, 15}, {7, 15}
+};
+
+
+/*
+ * Given the previous state and the current state, what input bit caused
+ * the transition? If it is impossible to transition between the two
+ * states, the value is 2.
+ *
+ * prev_next_state[previous_state][current_state]
+ */
+static const unsigned int prev_next_state[1 << (K - 1)][1 << (K - 1)] = {
+ { 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2},
+ { 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2},
+ { 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2},
+ { 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2},
+ { 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2},
+ { 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2},
+ { 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2},
+ { 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2},
+ { 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2},
+ { 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2},
+ { 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2},
+ { 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2},
+ { 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2},
+ { 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2},
+ { 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1},
+ { 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1}
+};
+
+
+static inline unsigned int hamming_distance2(unsigned int w) {
+
+ return (w & 1) + !!(w & 2);
+}
+
+
+/*
+static void conv_encode(unsigned char *data, unsigned char *output) {
+
+ unsigned int i, state = 0, o;
+
+ // encode data
+ for(i = 0; i < CONV_INPUT_SIZE; i++) {
+ o = encode[state][data[i]];
+ state = next_state[state][data[i]];
+ *output++ = !!(o & 2);
+ *output++ = o & 1;
+ }
+}
+ */
+
+
+static int conv_decode(unsigned char *output, unsigned char *data) {
+
+ int i, t;
+ unsigned int rdata, state, nstate, b, o, distance, accumulated_error,
+ min_state, min_error, cur_state;
+
+ unsigned int ae[1 << (K - 1)];
+ unsigned int nae[1 << (K - 1)]; // next accumulated error
+ unsigned int state_history[1 << (K - 1)][CONV_INPUT_SIZE + 1];
+
+ // initialize accumulated error, assume starting state is 0
+ for(i = 0; i < (1 << (K - 1)); i++)
+ ae[i] = nae[i] = MAX_ERROR;
+ ae[0] = 0;
+
+ // build trellis
+ for(t = 0; t < CONV_INPUT_SIZE; t++) {
+
+ // get received data symbol
+ rdata = (data[2 * t] << 1) | data[2 * t + 1];
+
+ // for each state
+ for(state = 0; state < (1 << (K - 1)); state++) {
+
+ // make sure this state is possible
+ if(ae[state] >= MAX_ERROR)
+ continue;
+
+ // find all states we lead to
+ for(b = 0; b < 2; b++) {
+
+ // get next state given input bit b
+ nstate = next_state[state][b];
+
+ // find output for this transition
+ o = encode[state][b];
+
+ // calculate distance from received data
+ distance = hamming_distance2(rdata ^ o);
+
+ // choose surviving path
+ accumulated_error = ae[state] + distance;
+ if(accumulated_error < nae[nstate]) {
+
+ // save error for surviving state
+ nae[nstate] = accumulated_error;
+
+ // update state history
+ state_history[nstate][t + 1] = state;
+ }
+ }
+ }
+
+ // get accumulated error ready for next time slice
+ for(i = 0; i < (1 << (K - 1)); i++) {
+ ae[i] = nae[i];
+ nae[i] = MAX_ERROR;
+ }
+ }
+
+ // the final state is the state with the fewest errors
+ min_state = (unsigned int)-1;
+ min_error = MAX_ERROR;
+ for(i = 0; i < (1 << (K - 1)); i++) {
+ if(ae[i] < min_error) {
+ min_state = i;
+ min_error = ae[i];
+ }
+ }
+
+ // trace the path
+ cur_state = min_state;
+ for(t = CONV_INPUT_SIZE; t >= 1; t--) {
+ min_state = cur_state;
+ cur_state = state_history[cur_state][t]; // get previous
+ output[t - 1] = prev_next_state[cur_state][min_state];
+ }
+
+ // return the number of errors detected (hard-decision)
+ return min_error;
+}
+
+
+/*
+ * GSM SACCH interleaving and burst mapping
+ *
+ * Interleaving:
+ *
+ * Given 456 coded input bits, form 4 blocks of 114 bits:
+ *
+ * i(B, j) = c(n, k) k = 0, ..., 455
+ * n = 0, ..., N, N + 1, ...
+ * B = B_0 + 4n + (k mod 4)
+ * j = 2(49k mod 57) + ((k mod 8) div 4)
+ *
+ * Mapping on Burst:
+ *
+ * e(B, j) = i(B, j)
+ * e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56
+ * e(B, 57) = h_l(B)
+ * e(B, 58) = h_n(B)
+ *
+ * Where h_l(B) and h_n(B) are bits in burst B indicating flags.
+ */
+
+/*
+static void interleave(unsigned char *data, unsigned char *iBLOCK) {
+
+ int j, k, B;
+
+ // for each bit in input data
+ for(k = 0; k < CONV_SIZE; k++) {
+ B = k % 4;
+ j = 2 * ((49 * k) % 57) + ((k % 8) / 4);
+ iBLOCK[B * iBLOCK_SIZE + j] = data[k];
+ }
+}
+ */
+
+
+#if 0
+static void decode_interleave(unsigned char *data, unsigned char *iBLOCK) {
+
+ int j, k, B;
+
+ for(k = 0; k < CONV_SIZE; k++) {
+ B = k % 4;
+ j = 2 * ((49 * k) % 57) + ((k % 8) / 4);
+ data[k] = iBLOCK[B * iBLOCK_SIZE + j];
+ }
+}
+
+#endif
+
+/*
+static void burstmap(unsigned char *iBLOCK, unsigned char *eBLOCK,
+ unsigned char hl, unsigned char hn) {
+
+ int j;
+
+ for(j = 0; j < 57; j++) {
+ eBLOCK[j] = iBLOCK[j];
+ eBLOCK[j + 59] = iBLOCK[j + 57];
+ }
+ eBLOCK[57] = hl;
+ eBLOCK[58] = hn;
+}
+ */
+
+
+static void decode_burstmap(unsigned char *iBLOCK, unsigned char *eBLOCK,
+ unsigned char *hl, unsigned char *hn) {
+
+ int j;
+
+ for(j = 0; j < 57; j++) {
+ iBLOCK[j] = eBLOCK[j];
+ iBLOCK[j + 57] = eBLOCK[j + 59];
+ }
+ *hl = eBLOCK[57];
+ *hn = eBLOCK[58];
+}
+
+
+/*
+ * Transmitted bits are sent least-significant first.
+ */
+static int compress_bits(unsigned char *dbuf, unsigned int dbuf_len,
+ unsigned char *sbuf, unsigned int sbuf_len) {
+
+ unsigned int i, j, c, pos = 0;
+
+ if(dbuf_len < ((sbuf_len + 7) >> 3))
+ return -1;
+
+ for(i = 0; i < sbuf_len; i += 8) {
+ for(j = 0, c = 0; (j < 8) && (i + j < sbuf_len); j++)
+ c |= (!!sbuf[i + j]) << j;
+ dbuf[pos++] = c & 0xff;
+ }
+ return pos;
+}
+
+
+#if 0
+int get_ns_l3_len(unsigned char *data, unsigned int datalen) {
+
+ if((data[0] & 3) != 1) {
+ fprintf(stderr, "error: get_ns_l3_len: pseudo-length reserved "
+ "bits bad (%2.2x)\n", data[0] & 3);
+ return -1;
+ }
+ return (data[0] >> 2);
+}
+
+#endif
+
+
+static unsigned char *decode_sacch(GS_CTX *ctx, unsigned char *burst, unsigned int *datalen) {
+
+ int errors, len, data_size;
+ unsigned char conv_data[CONV_SIZE], iBLOCK[BLOCKS][iBLOCK_SIZE],
+ hl, hn, decoded_data[PARITY_OUTPUT_SIZE];
+ FC_CTX fc_ctx;
+
+ data_size = sizeof ctx->msg;
+ if(datalen)
+ *datalen = 0;
+
+ // unmap the bursts
+ decode_burstmap(iBLOCK[0], burst, &hl, &hn); // XXX ignore stealing bits
+ decode_burstmap(iBLOCK[1], burst + 116, &hl, &hn);
+ decode_burstmap(iBLOCK[2], burst + 116 * 2, &hl, &hn);
+ decode_burstmap(iBLOCK[3], burst + 116 * 3, &hl, &hn);
+
+ // remove interleave
+ interleave_decode(&ctx->interleave_ctx, conv_data, (unsigned char *)iBLOCK);
+ //decode_interleave(conv_data, (unsigned char *)iBLOCK);
+
+ // Viterbi decode
+ errors = conv_decode(decoded_data, conv_data);
+ if (errors) {
+ DEBUGF("conv_decode: %d\n", errors);
+ return NULL;
+ }
+
+ // check parity
+ // If parity check error detected try to fix it.
+ if (parity_check(decoded_data))
+ {
+ FC_init(&fc_ctx, 40, 184);
+ unsigned char crc_result[224];
+ if (FC_check_crc(&fc_ctx, decoded_data, crc_result) == 0)
+ {
+ errors = -1;
+ DEBUGF("error: sacch: parity error (%d)\n", errors);
+ return NULL;
+ } else {
+ DEBUGF("Successfully corrected parity bits!\n");
+ memcpy(decoded_data, crc_result, sizeof crc_result);
+ errors = 0;
+ }
+ }
+
+ if((len = compress_bits(ctx->msg, data_size, decoded_data,
+ DATA_BLOCK_SIZE)) < 0) {
+ fprintf(stderr, "error: compress_bits\n");
+ return NULL;
+ }
+ if(len < data_size) {
+ fprintf(stderr, "error: buf too small (%d < %d)\n",
+ sizeof(ctx->msg), len);
+ return NULL;
+ }
+
+ if(datalen)
+ *datalen = (unsigned int)len;
+ return ctx->msg;
+}
+
+
+/*
+ * decode_cch
+ *
+ * Decode a "common" control channel. Most control channels use
+ * the same burst, interleave, Viterbi and parity configuration.
+ * The documentation for the control channels defines SACCH first
+ * and then just keeps referring to that.
+ *
+ * The current (investigated) list is as follows:
+ *
+ * BCCH Norm
+ * BCCH Ext
+ * PCH
+ * AGCH
+ * CBCH (SDCCH/4)
+ * CBCH (SDCCH/8)
+ * SDCCH/4
+ * SACCH/C4
+ * SDCCH/8
+ * SACCH/C8
+ *
+ * We provide two functions, one for where all four bursts are
+ * contiguous, and one where they aren't.
+ */
+unsigned char *decode_cch(GS_CTX *ctx, unsigned char *burst, unsigned int *datalen) {
+
+ return decode_sacch(ctx, burst, datalen);
+}
+
+
+#if 0
+unsigned char *decode_cch(GS_CTX *ctx, unsigned char *e, unsigned int *datalen) {
+
+ return decode_sacch(ctx, e, e + eBLOCK_SIZE, e + 2 * eBLOCK_SIZE,
+ e + 3 * eBLOCK_SIZE, datalen);
+}
+#endif
diff --git a/gsmstack/channel.c b/gsmstack/channel.c
new file mode 100644
index 0000000..768ed65
--- /dev/null
+++ b/gsmstack/channel.c
@@ -0,0 +1,143 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "gsmstack.h"
+
+/* convert an 18byte 8-bit-per-byte burst to a 142byte 1bit-per-byte */
+static void bit_per_byte(unsigned char *dest, const unsigned char *src)
+{
+ int bit, byte;
+
+ for (byte = 0; byte < BURST_BYTES; src++) {
+ for (bit = 0; bit < 8; bit++) {
+ if (src[byte] >> bit &= 0x01)
+ *dest++ = 0x01;
+ else
+ *dest++ = 0x00;
+ }
+ }
+}
+
+static int add_burst_to_lchan(struct gsm_logi_chan *lchan,
+ struct gsm_burst *burst)
+{
+ int rc = 0;
+
+ /* copy burst to burst buffer */
+ memcpy(lchan->burst_buf[lchan->next_burst], burst, sizeof(*burst));
+ lchan->next_burst++;
+
+ switch (lchan->type) {
+ case GSM_LCHAN_TCH_F:
+ /* FIXME */
+ break;
+ case GSM_LCHAN_TCH_H:
+ /* FIXME */
+ default:
+ if (lchan->next_burst == 4) {
+ lchan->next_burst = 0;
+ /* FIXME: decode the four bursts into a MAC block */
+
+ /* pass the resulting MAC block up the stack */
+ rc = gsm_lchan_macblock()
+ }
+ }
+
+ return rc;
+}
+
+static int gsm_rx_sdcch8(struct gsm_burst *burst)
+{
+ struct gsm_phys_chan *pchan = burst->gsm_pchan;
+ int rc = -EINVAL;
+
+ if (burst_type == GSM_BURST_DUMMY)
+ return;
+
+ if (burst->type != GSM_BURST_NORMAL) {
+ fprintf(stderr, "Burst type %u not allowed in SDCCH8\n",
+ burst->type);
+ /* FIXME: statistics */
+ return rc;
+ }
+
+ lchan = get_lchan(pchan, burst->fnr);
+ return add_burst_to_lchan(lchan, burst);
+}
+
+static int gsm_rx_tch(struct gsm_burst *burst)
+{
+ struct gsm_phys_chan *pchan = burst->gsm_pchan;
+ struct gsm_logi_chan *lchan;
+ int rc = -EINVAL;
+
+ if (burst->type != GSM_BURST_NORMAL)
+ return rc;
+
+ lchan = get_lchan(pchan, fnr);
+ return add_burst_to_lchan(lchan, burst);
+}
+
+/* input a new GSM Um burst on a CCCH */
+static int gsm_rx_ccch(struct gsm_burst *burst)
+{
+ struct gsm_phys_chan *pchan = burst->gsm_pchan;
+ struct gsm_logi_chan *lchan;
+ int rc = -EINVAL;
+
+ switch (burst->type) {
+ case GSM_BURST_FCCH:
+ /* FIXME */
+ /* obtain the frequency offset and report to caller */
+ break;
+ case GSM_BURST_SCH:
+ /* obtain the RFN from the SCH burst */
+ /* FIXME */
+ break;
+ case GSM_BURST_NORMAL:
+ /* determine logical channel and append burst */
+ lchan = get_lchan(pchan, burst->fnr);
+ rc = add_burst_to_lchan(lchan, burst);
+ break;
+ default:
+ break;
+ };
+ return rc;
+}
+
+/* input a new GSM Um interface burst into the stack */
+int gsm_rx_burst(struct gsm_burst *burst, int bits)
+{
+ struct gsm_phys_chan *pchan;
+ int rc = -EINVAL;
+
+ /* we assume the following fields have already been
+ * filled-in by the caller:
+ * phys_chan, rx_time, rx_frame_nr, decoded/decoded_bits */
+
+ pchan = burst->phys_chan;
+
+ if (!bits)
+ bit_per_byte(burst->decoded_bits, burst->decoded);
+
+ pchan->stats.rx_total++;
+ pchan->stats.rx_type[burst->type]++;
+
+ switch (pchan->config) {
+ case GSM_PCHAN_CCCH:
+ rc = gsm_rx_ccch(burst);
+ break;
+ case GSM_PCHAN_SDCCH8_SACCH8C:
+ rc = gsm_rx_sdcch8(burst);
+ break;
+ case GSM_PCHAN_TCH_F:
+ rc =
+ break;
+ case GSM_PCHAN_UNKNOWN:
+ default:
+ fprintf(stderr, "unknown pchan config (ts=%u\n)\n",
+ pchan->timeslot);
+ return;
+ break;
+ }
+}
diff --git a/gsmstack/get_lctype.c b/gsmstack/get_lctype.c
new file mode 100644
index 0000000..953b56d
--- /dev/null
+++ b/gsmstack/get_lctype.c
@@ -0,0 +1,105 @@
+
+/* helper routines to determine the logical channel type based on
+ * physical channel configuration and frame number */
+
+#include "gsmstack.h"
+#include "get_lctype.h"
+
+#define GSM_FN_51 (fn / 51)
+
+#define GSM_TC(fn) ((fn / 51) % 8)
+
+
+/* parameters determined from CCCH_CONF (octet 2 of control channel description
+ * in System Information Type 3:
+ * BS_CC_CHANS defines if we have CCCH on ts 2/4/6
+ * BS_CCCH_SDCCH_COMB defines if we have SDCCH/8 SACCH/C8 on TS0
+ * BS_AG_BLKS_RES defines which CCCH blocks are reserved for AGCH
+ * if BCCH Ext. is used, BS_AS_BLKS_RES has to be non-zero
+ */
+
+static int get_lctype_for_ccch(unsigned int fnr)
+{
+ unsigned int fnr51 = GSM_FN_51(fnr);
+ int lc_type;
+
+ if (fnr51 % 10 == 0)
+ lc_type = GSM_LCHAN_FCCH;
+ else if (fnr51 % 10 == 1)
+ lc_type = GSM_LCHAN_SCH;
+ else if (fnr51 >= 2 && fnr_mod_51 <= 5)
+ lc_type = GSM_LCHAN_BCCH;
+ else if (fnr51 >= 6 && fnr51 <= 9) {
+ if (flags & CCCH_F_BCCH_EXT)
+ lc_type = GSM_LCHAN_BCCH;
+ else
+ lc_ctype = GSM_LCHAN_PCH;
+ } else
+ lc_ctype = GSM_LCHAN_PCH;
+
+ /* FIXME: what about AGCH ? */
+ /* FIXME: what about NCH ? */
+ /* FIXME: what about CBCH ? */
+
+ return lc_ctype;
+}
+
+static int get_lctype_for_sdcch8(unsigned int fnr)
+{
+ unsigned int fnr51 = GSM_FN_51(fnr);
+ unsigned int fnr102 = fnr % 102;
+ int lc_type;
+
+ /* the lower 32 bursts are evenly divided between SDCCH8 0..7 */
+ if (fnr51 % < 32) {
+ lc_type = GSM_LCHAN_SDCCH8;
+ subc = fnr51 / 4;
+ } else {
+ /* the upper 16 bursts are bundles of four bursts for
+ * alternating either SACCH0..3 or SACCH4..7 */
+ lc_type = GSM_LCHAN_SACCH8C;
+ subc = (fnr51 - 32) / 4;
+ if (fnr102 > 50)
+ subc += 4;
+ }
+
+ return lc_type;
+}
+
+static int get_lctype_for_tch_f(unsigned int fnr)
+{
+ unsigned int fnr52 = fnr % 52;
+ int lc_type = GSM_LCHAN_TCH;
+
+ /* only burst number 12 and 51 are be SACCH */
+ if (fnr52 == 12 || fnr52 == 51)
+ lc_ctype = GSM_LCHAN_SACCH;
+ /* burst number 26 and 39 are empty (for measurements) */
+ else if (fnr52 == 26 || fnr52 == 39)
+ lc_ctype = GSM_LCHAN_NONE;
+
+ return lc_type;
+}
+
+int get_lctype(struct gsm_phys_chan *pchan, int fnr)
+{
+ switch (pchan->config) {
+ case GSM_PCHAN_CCCH:
+ return get_lctype_for_ccch(fnr);
+ break;
+ case GSM_PCHAN_TCH_F:
+ return get_lctype_for_tch_f(fnr);
+ break;
+ case GSM_PCHAN_SDCCH8_SACCH8C:
+ return get_lctype_for_sdcch8(fnr);
+ break;
+ }
+
+ return -EINVAL;
+}
+
+struct gsm_logi_chan *get_lchan(struct gsm_phys_chan *pchan, int fnr)
+{
+ int lctype = get_lctype(pchan, fnr);
+ return pchan->logi_chan[lctype];
+}
diff --git a/gsmstack/get_lctype.h b/gsmstack/get_lctype.h
new file mode 100644
index 0000000..32164a4
--- /dev/null
+++ b/gsmstack/get_lctype.h
@@ -0,0 +1,7 @@
+#ifndef _GSM_LCTYPE_H
+#define _GSM_LCTYPE_H
+
+extern int get_lctype(struct gsm_phys_chan *pchan, int fnr);
+extern struct gsm_logi_chan *get_lchan(struct gsm_phys_chan *pchan, int fnr);
+
+#endif /* _GSM_LCTYPE_H */
diff --git a/gsmstack/gsmstack.c b/gsmstack/gsmstack.c
new file mode 100644
index 0000000..1690d15
--- /dev/null
+++ b/gsmstack/gsmstack.c
@@ -0,0 +1,31 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#include <errno.h>
+
+#include "gsmstack.h"
+
+
+struct gsm_rf_chan *gsm_init_rfchan(unsigned int arfcn)
+{
+ struct gsm_rf_chan *rf;
+ int i;
+
+ rf = malloc(sizeof(*rf));
+ if (!rf)
+ return NULL;
+ memset(rf, 0, sizeof(*rf));
+
+ rf->arfcn = arfcn;
+
+ for (i = 0; i < NR_TIMESLOTS; i++) {
+ struct gsm_phys_chan *pchan;
+
+ pchan = rf->phys_chan[i];
+ pchan->timeslot = i;
+ pchan->rf_chan = rf;
+ }
+
+ return rf;
+}
diff --git a/gsmstack/gsmstack.h b/gsmstack/gsmstack.h
new file mode 100644
index 0000000..9986dff
--- /dev/null
+++ b/gsmstack/gsmstack.h
@@ -0,0 +1,133 @@
+#ifndef _GSMSTACK_H
+#define _GSMSTACK_H
+
+#define NR_TIMESLOTS 8
+#define NR_ARFCN 1024
+
+#define BURST_BITS (USEFUL_BITS/8 + 1)
+
+#include "gsmtap.h"
+enum gsm_burst_type {
+ GSM_BURST_UNKNOWN = GSMTAP_BURST_UNKNOWN,
+ GSM_BURST_FCCH = GSMTAP_BURST_FCCH,
+ GSM_BURST_PARTIAL_FCCH = GSMTAP_BURST_PARTIAL_FCCH,
+ GSM_BURST_SCH = GSMTAP_BURST_SCH,
+ GSM_BURST_CTS_SCH = GSMTAP_BURST_CTS_SCH,
+ GSM_BURST_COMPACT_SCH = GSMTAP_BURST_COMPACT_SCH,
+ GSM_BURST_NORMAL = GSMTAP_BURST_NORMAL,
+ GSM_BURST_DUMMY = GSMTAP_BURST_DUMMY,
+ GSM_BURST_ACCESS = GSMTAP_BURST_ACCESS,
+ GSM_BURST_NONE = GSMTAP_BURST_NONE,
+ _GSM_BURST_CNT
+};
+
+
+struct gsm_burst {
+ /* time at which we were received */
+ struct timeval rx_time;
+
+ /* the physical channel which we're part of.
+ * always guaranteed to be != NULL */
+ struct gsm_phys_chan *phys_chan;
+
+ /* the logical channel to which we belong.
+ * only filled-in if we actually know about it */
+ struct gsm_logi_chan *logi_chan;
+
+ /* the burst type (as per gsmtap.h) */
+ unsigned char burst_type;
+
+ /* the relative receive TDMA frame */
+ unsigned int rx_frame_nr;
+
+ /* the timeslot number is part of the phys_chan */
+
+ /* the burst after differential decode, 8 bit per byte */
+ unsigned char decoded[BURST_BITS];
+
+ /* the burst after differential decode, 1 bit per byte */
+ unsigned char decoded_bits[USEFUL_BITS];
+};
+
+struct gsm_burst_stats {
+ unsigned int rx_total;
+ unsigned int rx_type[_GSM_BURST_CNT];
+};
+
+enum gsm_logical_channel_type {
+ GSM_LCHAN_UNKNOWN,
+ GSM_LCHAN_NONE,
+
+ /* CBCH */
+ GSM_LCHAN_FCCH, /* Frequency Correction CH */
+ GSM_LCHAN_SCH, /* Synchronization CH */
+ GSM_LCHAN_BCCH, /* Broadcast Control CH */
+ GSM_LCHAN_PCH, /* Paging CH */
+ GSM_LCHAN_NCH, /* Notification CH */
+ GSM_LCHAN_AGCH, /* Access Grant CH */
+ GSM_LCHAN_CBCH, /* Cell Broadcast CH */
+
+ /* SDCCH */
+ GSM_LCHAN_SDCCH8, /* Slow Dedicated Control CH */
+ GSM_LCHAN_SACCH8C, /* Slow Associated Control CH */
+
+ /* TCH */
+ GSM_LCHAN_TCH, /* Traffic CH */
+ GSM_LCHAN_SACCH, /* Slow Associated Control CH */
+
+ /* uplink */
+ GSM_LCHAN_RACH, /* Random Access CH */
+};
+
+struct gsm_logi_chan {
+ enum gsm_logical_channel_type type;
+ /* here we aggregate the bursts for this logical channel
+ * until we have found enough bursts for one MAC block */
+ struct gsm_burst burst_buf[4];
+ int next_burst;
+
+ struct gsm_burst_stats stats;
+};
+
+enum gsm_phys_chan_config {
+ GSM_PCHAN_UNKNOWN,
+ GSM_PCHAN_CCCH,
+ GSM_PCHAN_CCCH_WITH_SDCCH8,
+ GSM_PCHAN_TCH_F,
+ GSM_PCHAN_TCH_H,
+ GSM_PCHAN_SDCCH8_SACCH8C,
+};
+
+/* maximum logical channels for one physical channel */
+#define NR_LOGI_CHANS 16
+
+/* A GSM physical channel configuration */
+struct gsm_phys_chan {
+ /* in which timeslot is this channel? */
+ unsigned int timeslot;
+ /* to which RF channel do we belong? */
+ struct gsm_rf_chan *rf_chan;
+ /* how is our physical configuration */
+ enum gsm_phys_chan_config config;
+ /* those are our logical channels */
+ struct gsm_logi_chan logi_chan[NR_LOGI_CHANS];
+ int nr_logi_chans;
+
+ struct gsm_burst_stats stats;
+};
+
+struct gsm_rf_chan {
+ /* ARFCN (frequency) of the RF channel */
+ unsigned int arfcn;
+
+ /* current RFN as determined by SCH + frame count */
+ unsigned int rfn;
+
+ /* the physical channel for each timeslot */
+ struct gsm_phys_chan phys_chan[NR_TIMESLOTS];
+};
+
+
+extern struct gsm_rf_chan *gsm_init_rfchan(unsigned int arfcn);
+
+#endif /* _GSMSTACK_H */
diff --git a/gsmstack/gsmstack_chan.h b/gsmstack/gsmstack_chan.h
new file mode 100644
index 0000000..a54fae0
--- /dev/null
+++ b/gsmstack/gsmstack_chan.h
@@ -0,0 +1,12 @@
+#ifndef _GSMSTACK_CHAN_H
+#define _GSMSTACK_CHAN_H
+
+/* 51 multiframe for CCCH TS0 of a FCCH+SCH+BCCH+PACH */
+const char ccch[] =
+ "FSBBBBPPPPFSPPPPPPPPFSPPPPPPPPFSPPPPPPPPFSPPPPPPPP_";
+
+/* SDCCH/8 + SACCH/C8; A...D are alternating 0..3/4..7 */
+const char sdcch8[] =
+ "00001111222233334444555566667777AAAABBBBCCCCDDDD___";
+
+#endif /* _GSMSTACK_CHAN_H */
diff --git a/gsmstack/gsmtap.h b/gsmstack/gsmtap.h
new file mode 100644
index 0000000..20a6074
--- /dev/null
+++ b/gsmstack/gsmtap.h
@@ -0,0 +1,57 @@
+#ifndef _GSMTAP_H
+#define _GSMTAP_H
+
+/* gsmtap header, pseudo-header in front of the actua GSM payload*/
+
+#include <sys/types.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
+
+struct gsmtap_hdr {
+ u_int8_t version; /* version, set to 0x01 currently */
+ u_int8_t hdr_len; /* length in number of 32bit words */
+ u_int8_t type; /* see GSMTAP_TYPE_* */
+ u_int8_t timeslot; /* timeslot (0..7 on Um) */
+
+ u_int16_t arfcn; /* ARFCN (frequency) */
+ u_int8_t noise_db; /* noise figure in dB */
+ u_int8_t signal_db; /* signal level in dB */
+
+ u_int32_t frame_number; /* GSM Frame Number (FN) */
+
+ u_int8_t burst_type; /* Type of burst, see above */
+ u_int8_t antenna_nr; /* Antenna Number */
+ u_int16_t res; /* reserved for future use (RFU) */
+
+} __attribute__((packed));
+
+
+/* PCAP related definitions */
+#define TCPDUMP_MAGIC 0xa1b2c3d4
+struct pcap_timeval {
+ int32_t tv_sec;
+ int32_t tv_usec;
+};
+
+struct pcap_sf_pkthdr {
+ struct pcap_timeval ts; /* time stamp */
+ u_int32_t caplen; /* lenght of portion present */
+ u_int32_t len; /* length of this packet */
+};
+
+#endif /* _GSMTAP_H */
diff --git a/gsmstack/source_pcap_burst.c b/gsmstack/source_pcap_burst.c
new file mode 100644
index 0000000..bb340e7
--- /dev/null
+++ b/gsmstack/source_pcap_burst.c
@@ -0,0 +1,148 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pcap.h>
+
+#include "gsmstack.h"
+#include "gsmtap.h"
+
+/* source_pcap_burst.c: read a PCAP file with GSMTAP_UM_BURST packets
+ * and feed them into the stack.
+ *
+ * (C) 2008 by Harald Welte <laforge@gnumonks.org>
+ *
+ * TODO:
+ * fix PCAP endianness
+ */
+
+
+static struct gsm_rf_chan rfchans[NR_ARFCN];
+
+static int read_pcap_hdr(int fd)
+{
+ int rc;
+ struct pcap_file_header pfh;
+
+ rc = read(fd, &pfh, sizeof(pfh));
+ if (rc < sizeof(pfh))
+ return -EIO;
+
+ if (pfh.magic != TCPDUMP_MAGIC)
+ return -EINVAL;
+
+ if (pfh.linktype != LINKTYPE_GSMTAP)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int send_burst(struct gsmtap_hdr *gh, int burst_len,
+ struct timeval *tv)
+{
+ unsigned char *data = (unsigned char *)gh + sizeof(*gh);
+ struct gsm_burst burst;
+ struct gsm_rf_chan *rfchan;
+ struct gsm_phys_chan *pchan;
+ unsigned int arfcn;
+
+ arfcn = ntohs(gh->arfcn);
+
+ /* Create new RF channel structure if we've not seen this ARFCN
+ * before */
+ if (!rfchans[arfcn]) {
+ rfchans[arfcn] = gsm_init_rfchan(arfcn);
+ if (!rfchans[arfcn]) {
+ fprintf(stderr, "cannot init rfchan ARFCN=%u\n",
+ arfcn);
+ return -EINVAL;
+ }
+ }
+
+ rfchan = rfchans[arfcn];
+ pchan = rfchan->phys_chan[gh->timeslot];
+
+ memset(&burst, 0, sizeof(burst));
+ burst.phys_chan = pchan;
+ burst.burst_type = gh->burst_type;
+ burst.rx_frame_nr = ntohl(gh->frame_number);
+ memcpy(&burst.rx_time, tv, sizeof(burst.rx_time));
+ memcpy(burst.decoded, data, burst_len);
+
+ return gsm_rx_burst(&burst, 0);
+}
+
+/* fills 'buf' with gsmtap_hdr and payload, returns length of payload */
+static int read_one_pkt(int fd, unsigned char *buf, int bufsize,
+ struct timeval *tv)
+{
+ struct pcap_sf_pkthdr pkthdr;
+ struct gsmtap_hdr *gh = (struct gsmtap_hdr *) buf;
+ int len, burstlen;
+
+ len = read(fd, &pkthdr, sizeof(pkthdr));
+ if (len < sizeof(pkthdr))
+ return -1;
+
+ /* FIXME: ntohl on caplen and len? */
+
+ if (pkthdr.caplen < sizeof(*gh))
+ return -2;
+
+ if (pkthdr.len > pkthdr.caplen)
+ return -3;
+
+ if (bufsize < pkthdr.len)
+ return -4;
+
+ if (tv) {
+ tv.tv_sec = pkthdr.tv_sec;
+ tv.tv_usec = pkthdr.tv_usec;
+ }
+
+ len = read(fd, buf, pkthdr.caplen)
+ if (len < pkthdr.caplen)
+ return -5;
+
+ if (gh->version != GSMTAP_VERSION ||
+ gh->type != GSMTAP_TYPE_UM_BURST)
+ return -6;
+
+ return pkthdr.caplen - sizeof(*gh);
+}
+
+int main(int argc, char **argv)
+{
+ char *fname;
+ int fd, rc;
+
+ if (argc != 2) {
+ fprintf(stderr, "please specify pcap filename\n");
+ exit(2);
+ }
+
+ fname = argv[1];
+
+ fd = open(fname, O_RDONLY);
+ if (fd < 0) {
+ perror("open");
+ exit(1);
+ }
+
+ rc = read_pcap_hdr(fd);
+ if (rc < 0) {
+ perror("pcap_hdr");
+ exit(1);
+ }
+
+ while (1) {
+ unsigned char buf[1024];
+ struct timeval tv;
+ int burst_len;
+ burst_len = read_one_pkt(fd, &tv, sbuf, sizeof(buf));
+ if (burst_len < 0) {
+ fprintf(stderr, "read_one_pkt() = %d\n", burst_len);
+ exit(3);
+ }
+ rc = send_burst((struct gsmtap_hdr *)buf, burst_len, &tv);
+ }
+}
personal git repositories of Harald Welte. Your mileage may vary