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.
This commit is contained in:
Harald Welte 2008-12-01 01:19:19 +05:30
parent 753c2fc0f6
commit fd405f7994
9 changed files with 1119 additions and 0 deletions

483
gsmstack/cch.c Normal file
View File

@ -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

143
gsmstack/channel.c Normal file
View File

@ -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;
}
}

105
gsmstack/get_lctype.c Normal file
View File

@ -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];
}

7
gsmstack/get_lctype.h Normal file
View File

@ -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 */

31
gsmstack/gsmstack.c Normal file
View File

@ -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;
}

133
gsmstack/gsmstack.h Normal file
View File

@ -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 */

12
gsmstack/gsmstack_chan.h Normal file
View File

@ -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 */

57
gsmstack/gsmtap.h Normal file
View File

@ -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 */

View File

@ -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);
}
}