aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2009-06-10 23:21:25 +0800
committerHarald Welte <laforge@gnumonks.org>2009-06-10 23:21:25 +0800
commitec44e1ff4139f60483dfcaa8329e39b41bfb8c4a (patch)
tree1336fce24e2c2550993ba4ef20ab76cfe8ca196f
parent0c3893078ed3f38f76b0236627bb38a8bd906af6 (diff)
parent20152a35e35e790ae281cd2445b746e338eb669c (diff)
Merge commit 'origin/master'
-rw-r--r--openbsc/configure.in1
-rw-r--r--openbsc/include/openbsc/gsm_data.h1
-rw-r--r--openbsc/include/openbsc/gsm_subscriber.h7
-rw-r--r--openbsc/include/openbsc/paging.h2
-rw-r--r--openbsc/include/openbsc/signal.h1
-rw-r--r--openbsc/src/abis_nm.c3
-rw-r--r--openbsc/src/bsc_hack.c34
-rw-r--r--openbsc/src/e1_input.c11
-rw-r--r--openbsc/src/gsm_04_08.c34
-rw-r--r--openbsc/src/gsm_subscriber.c119
-rw-r--r--openbsc/src/paging.c49
-rw-r--r--openbsc/tests/Makefile.am2
-rw-r--r--openbsc/tests/channel/Makefile.am14
-rw-r--r--openbsc/tests/channel/channel_test.c72
14 files changed, 307 insertions, 43 deletions
diff --git a/openbsc/configure.in b/openbsc/configure.in
index b886e7ac1..94fb15f4d 100644
--- a/openbsc/configure.in
+++ b/openbsc/configure.in
@@ -45,4 +45,5 @@ AC_OUTPUT(
tests/sms/Makefile
tests/gsm0408/Makefile
tests/db/Makefile
+ tests/channel/Makefile
Makefile)
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index e85adf829..1fb80a2f3 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -25,6 +25,7 @@ enum gsm_hooks {
enum gsm_paging_event {
GSM_PAGING_SUCCEEDED,
GSM_PAGING_EXPIRED,
+ GSM_PAGING_OOM,
};
struct msgb;
diff --git a/openbsc/include/openbsc/gsm_subscriber.h b/openbsc/include/openbsc/gsm_subscriber.h
index 1ca79e2ae..780d8ede0 100644
--- a/openbsc/include/openbsc/gsm_subscriber.h
+++ b/openbsc/include/openbsc/gsm_subscriber.h
@@ -31,6 +31,10 @@ struct gsm_subscriber {
u_int8_t classmark2[3];
u_int8_t classmark3_len;
u_int8_t classmark3[14];
+
+ /* pending requests */
+ int in_callback;
+ struct llist_head requests;
};
enum gsm_subscriber_field {
@@ -51,6 +55,9 @@ struct gsm_subscriber *subscr_get_by_imsi(const char *imsi);
struct gsm_subscriber *subscr_get_by_extension(const char *ext);
int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason);
void subscr_put_channel(struct gsm_lchan *lchan);
+void subscr_get_channel(struct gsm_subscriber *subscr,
+ struct gsm_network *network, int type,
+ gsm_cbfn *cbfn, void *param);
/* internal */
struct gsm_subscriber *subscr_alloc(void);
diff --git a/openbsc/include/openbsc/paging.h b/openbsc/include/openbsc/paging.h
index de512d1ae..2f17e243e 100644
--- a/openbsc/include/openbsc/paging.h
+++ b/openbsc/include/openbsc/paging.h
@@ -33,7 +33,7 @@
void paging_init(struct gsm_bts *bts);
/* schedule paging request */
-void paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
+void paging_request(struct gsm_network *network, struct gsm_subscriber *subscr,
int type, gsm_cbfn *cbfn, void *data);
/* stop paging requests */
diff --git a/openbsc/include/openbsc/signal.h b/openbsc/include/openbsc/signal.h
index 4a583f689..c065f9003 100644
--- a/openbsc/include/openbsc/signal.h
+++ b/openbsc/include/openbsc/signal.h
@@ -54,6 +54,7 @@ enum signal_abisip {
enum signal_nm {
S_NM_SW_ACTIV_REP, /* GSM 12.21 software activated report */
S_NM_FAIL_REP, /* GSM 12.21 failure event report */
+ S_NM_NACK, /* GSM 12.21 various NM_MT_*_NACK happened */
};
/* SS_LCHAN signals */
diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c
index 74dba2377..83e6bbb48 100644
--- a/openbsc/src/abis_nm.c
+++ b/openbsc/src/abis_nm.c
@@ -893,6 +893,9 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
else
DEBUGPC(DNM, "\n");
+
+ dispatch_signal(SS_NM, S_NM_NACK, (void*) ((long)mt));
+ return 0;
}
#if 0
/* check if last message is to be acked */
diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c
index 7aa8b9aef..388840793 100644
--- a/openbsc/src/bsc_hack.c
+++ b/openbsc/src/bsc_hack.c
@@ -454,6 +454,18 @@ static int sw_activ_rep(struct msgb *mb)
return 0;
}
+/* Callback function for NACK on the OML NM */
+static int oml_msg_nack(int mt)
+{
+ if (mt == NM_MT_SET_BTS_ATTR_NACK) {
+ fprintf(stderr, "Failed to set BTS attributes. That is fatal. "
+ "Was the bts type and frequency properly specified?\n");
+ exit(-1);
+ }
+
+ return 0;
+}
+
/* Callback function to be called every time we receive a signal from NM */
static int nm_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
@@ -461,6 +473,8 @@ static int nm_sig_cb(unsigned int subsys, unsigned int signal,
switch (signal) {
case S_NM_SW_ACTIV_REP:
return sw_activ_rep(signal_data);
+ case S_NM_NACK:
+ return oml_msg_nack((int)signal_data);
default:
break;
}
@@ -960,6 +974,26 @@ static int bootstrap_network(void)
{
struct gsm_bts *bts;
+ switch(BTS_TYPE) {
+ case GSM_BTS_TYPE_NANOBTS_1800:
+ if (ARFCN < 512 || ARFCN > 885) {
+ fprintf(stderr, "GSM1800 channel must be between 512-885.\n");
+ return -EINVAL;
+ }
+ break;
+ case GSM_BTS_TYPE_BS11:
+ case GSM_BTS_TYPE_NANOBTS_900:
+ /* Assume we have a P-GSM900 here */
+ if (ARFCN < 1 || ARFCN > 124) {
+ fprintf(stderr, "GSM900 channel must be between 1-124.\n");
+ return -EINVAL;
+ }
+ break;
+ case GSM_BTS_TYPE_UNKNOWN:
+ fprintf(stderr, "Unknown BTS. Please use the --bts-type switch\n");
+ return -EINVAL;
+ }
+
/* initialize our data structures */
gsmnet = gsm_network_init(2, BTS_TYPE, MCC, MNC);
if (!gsmnet)
diff --git a/openbsc/src/e1_input.c b/openbsc/src/e1_input.c
index c3c7c7597..034bd9723 100644
--- a/openbsc/src/e1_input.c
+++ b/openbsc/src/e1_input.c
@@ -140,6 +140,7 @@ static void write_pcap_packet(int direction, int sapi, int tei,
int ret;
time_t cur_time;
struct tm *tm;
+ int mi_head = (direction==PCAP_INPUT) ? MISDN_HEADER_LEN : 0;
struct fake_linux_lapd_header header = {
.pkttype = 4,
@@ -163,13 +164,15 @@ static void write_pcap_packet(int direction, int sapi, int tei,
.ts_usec = 0,
.incl_len = msg->len + sizeof(struct fake_linux_lapd_header)
+ sizeof(struct lapd_header)
- - MISDN_HEADER_LEN,
+ - mi_head,
.orig_len = msg->len + sizeof(struct fake_linux_lapd_header)
+ sizeof(struct lapd_header)
- - MISDN_HEADER_LEN,
+ - mi_head,
};
+ printf("Packet of: %d\n", direction);
+
cur_time = time(NULL);
tm = localtime(&cur_time);
payload_header.ts_sec = mktime(tm);
@@ -177,8 +180,8 @@ static void write_pcap_packet(int direction, int sapi, int tei,
ret = write(pcap_fd, &payload_header, sizeof(payload_header));
ret = write(pcap_fd, &header, sizeof(header));
ret = write(pcap_fd, &lapd_header, sizeof(lapd_header));
- ret = write(pcap_fd, msg->data + MISDN_HEADER_LEN,
- msg->len - MISDN_HEADER_LEN);
+ ret = write(pcap_fd, msg->data + mi_head,
+ msg->len - mi_head);
}
static const char *sign_types[] = {
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index 052991c3b..90b88dfb3 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -1022,11 +1022,6 @@ static int mm_rx_id_resp(struct msgb *msg)
DEBUGP(DMM, "IDENTITY RESPONSE: mi_type=0x%02x MI(%s)\n",
mi_type, mi_string);
- /*
- * Rogue messages could trick us but so is life
- */
- put_lchan(lchan);
-
switch (mi_type) {
case GSM_MI_TYPE_IMSI:
if (!lchan->subscr)
@@ -1116,7 +1111,6 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
switch (mi_type) {
case GSM_MI_TYPE_IMSI:
/* we always want the IMEI, too */
- use_lchan(lchan);
rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMEI);
lchan->loc_operation->waiting_for_imei = 1;
@@ -1125,7 +1119,6 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
break;
case GSM_MI_TYPE_TMSI:
/* we always want the IMEI, too */
- use_lchan(lchan);
rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMEI);
lchan->loc_operation->waiting_for_imei = 1;
@@ -1133,7 +1126,6 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
subscr = subscr_get_by_tmsi(mi_string);
if (!subscr) {
/* send IDENTITY REQUEST message to get IMSI */
- use_lchan(lchan);
rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMSI);
lchan->loc_operation->waiting_for_imsi = 1;
}
@@ -1460,7 +1452,6 @@ static int gsm48_rr_rx_pag_resp(struct msgb *msg)
u_int8_t mi_type = mi_lv[1] & GSM_MI_TYPE_MASK;
char mi_string[MI_SIZE];
struct gsm_subscriber *subscr;
- struct gsm_bts *bts;
struct paging_signal_data sig_data;
int rc = 0;
@@ -1508,18 +1499,6 @@ static int gsm48_rr_rx_pag_resp(struct msgb *msg)
/* Stop paging on the bts we received the paging response */
paging_request_stop(msg->trx->bts, subscr, msg->lchan);
- /* Stop paging on all other bts' */
- bts = NULL;
- do {
- bts = gsm_bts_by_lac(msg->trx->bts->network, subscr->lac, bts);
- if (!bts)
- break;
- if (bts == msg->trx->bts)
- continue;
- /* Stop paging */
- paging_request_stop(bts, subscr, NULL);
- } while (1);
-
/* FIXME: somehow signal the completion of the PAGING to
* the entity that requested the paging */
@@ -1754,7 +1733,6 @@ static int gsm48_cc_rx_setup(struct msgb *msg)
struct gsm48_hdr *gh = msgb_l3(msg);
unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
struct gsm_subscriber *called_subscr;
- struct gsm_bts *bts;
char called_number[(43-2)*2 + 1] = "\0";
struct tlv_parsed tp;
int ret;
@@ -1798,16 +1776,8 @@ static int gsm48_cc_rx_setup(struct msgb *msg)
call->called_subscr = called_subscr;
/* Start paging subscriber on all BTS in LAC of subscriber */
- bts = NULL;
- do {
- bts = gsm_bts_by_lac(msg->trx->bts->network,
- msg->lchan->subscr->lac, bts);
- if (!bts)
- break;
- /* Trigger paging */
- paging_request(bts, called_subscr, RSL_CHANNEED_TCH_F,
- setup_trig_pag_evt, call);
- } while (1);
+ subscr_get_channel(called_subscr, msg->trx->bts->network, RSL_CHANNEED_TCH_F,
+ setup_trig_pag_evt, call);
/* send a CALL PROCEEDING message to the MO */
ret = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
diff --git a/openbsc/src/gsm_subscriber.c b/openbsc/src/gsm_subscriber.c
index 3f608ec30..3062a6bef 100644
--- a/openbsc/src/gsm_subscriber.c
+++ b/openbsc/src/gsm_subscriber.c
@@ -25,14 +25,77 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <assert.h>
#include <openbsc/gsm_subscriber.h>
+#include <openbsc/paging.h>
#include <openbsc/debug.h>
+#include <openbsc/paging.h>
#include <openbsc/db.h>
-
LLIST_HEAD(active_subscribers);
+/*
+ * Struct for pending channel requests. This is managed in the
+ * llist_head requests of each subscriber. The reference counting
+ * should work in such a way that a subscriber with a pending request
+ * remains in memory.
+ */
+struct subscr_request {
+ struct llist_head entry;
+
+ /* back reference */
+ struct gsm_subscriber *subscr;
+
+ /* the requested channel type */
+ int channel_type;
+
+ /* the bts we have decided to use */
+ struct gsm_network *network;
+
+ /* the callback data */
+ gsm_cbfn *cbfn;
+ void *param;
+};
+
+/*
+ * We got the channel assigned and can now hand this channel
+ * over to one of our callbacks.
+ */
+static int subscr_paging_cb(unsigned int hooknum, unsigned int event,
+ struct msgb *msg, void *data, void *param)
+{
+ struct subscr_request *request;
+ struct gsm_subscriber *subscr = (struct gsm_subscriber *)param;
+
+ assert(!llist_empty(&subscr->requests));
+
+ /*
+ * FIXME: What to do with paging requests coming during
+ * this callback? We must be sure to not start paging when
+ * we have an active connection to a subscriber and to make
+ * the subscr_put_channel work as required...
+ */
+ request = (struct subscr_request *)subscr->requests.next;
+ llist_del(&request->entry);
+ subscr->in_callback = 1;
+ request->cbfn(hooknum, event, msg, data, request->param);
+ subscr->in_callback = 0;
+
+ free(request);
+ return 0;
+}
+
+static void subscr_send_paging_request(struct gsm_subscriber *subscr)
+{
+ struct subscr_request *request;
+ assert(!llist_empty(&subscr->requests));
+
+ request = (struct subscr_request *)subscr->requests.next;
+ paging_request(request->network, subscr, request->channel_type,
+ subscr_paging_cb, subscr);
+}
+
struct gsm_subscriber *subscr_alloc(void)
{
struct gsm_subscriber *s;
@@ -45,6 +108,8 @@ struct gsm_subscriber *subscr_alloc(void)
llist_add_tail(&s->entry, &active_subscribers);
s->use_count = 1;
+ INIT_LLIST_HEAD(&s->requests);
+
return s;
}
@@ -131,6 +196,42 @@ struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr)
return NULL;
}
+void subscr_get_channel(struct gsm_subscriber *subscr,
+ struct gsm_network *network, int type,
+ gsm_cbfn *cbfn, void *param)
+{
+ struct subscr_request *request;
+
+ request = (struct subscr_request *)malloc(sizeof(*request));
+ if (!request) {
+ if (cbfn)
+ cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_OOM,
+ NULL, NULL, param);
+ return;
+ }
+
+ memset(request, 0, sizeof(*request));
+ request->network = network;
+ request->subscr = subscr;
+ request->channel_type = type;
+ request->cbfn = cbfn;
+ request->param = param;
+
+ /*
+ * FIXME: We might be able to assign more than one
+ * channel, e.g. voice and SMS submit at the same
+ * time.
+ */
+ if (!subscr->in_callback && llist_empty(&subscr->requests)) {
+ /* add to the list, send a request */
+ llist_add_tail(&request->entry, &subscr->requests);
+ subscr_send_paging_request(subscr);
+ } else {
+ /* this will be picked up later, from subscr_put_channel */
+ llist_add_tail(&request->entry, &subscr->requests);
+ }
+}
+
void subscr_put_channel(struct gsm_lchan *lchan)
{
/*
@@ -139,5 +240,21 @@ void subscr_put_channel(struct gsm_lchan *lchan)
* of the lchan after having asked the next requestee to handle
* the channel.
*/
+ /*
+ * FIXME: is the lchan is of a different type we could still
+ * issue an immediate assignment for another channel and then
+ * close this one.
+ */
+ /*
+ * Currently we will drop the last ref of the lchan which
+ * will result in a channel release on RSL and we will start
+ * the paging. This should work most of the time as the MS
+ * will listen to the paging requests before we timeout
+ */
+
put_lchan(lchan);
+
+ if (lchan->subscr && !llist_empty(&lchan->subscr->requests))
+ subscr_send_paging_request(lchan->subscr);
}
+
diff --git a/openbsc/src/paging.c b/openbsc/src/paging.c
index f3bdf6973..8f15e1640 100644
--- a/openbsc/src/paging.c
+++ b/openbsc/src/paging.c
@@ -210,8 +210,8 @@ static void paging_T3113_expired(void *data)
paging_remove_request(&req->bts->paging, req);
}
-void paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
- int type, gsm_cbfn *cbfn, void *data)
+static void _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
+ int type, gsm_cbfn *cbfn, void *data)
{
struct gsm_bts_paging_state *bts_entry = &bts->paging;
struct gsm_paging_request *req;
@@ -237,9 +237,25 @@ void paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
bsc_schedule_timer(&bts_entry->work_timer, 1, 0);
}
+void paging_request(struct gsm_network *network, struct gsm_subscriber *subscr,
+ int type, gsm_cbfn *cbfn, void *data)
+{
+ struct gsm_bts *bts = NULL;
+
+ do {
+ bts = gsm_bts_by_lac(network, subscr->lac, bts);
+ if (!bts)
+ break;
+
+ /* Trigger paging */
+ _paging_request(bts, subscr, RSL_CHANNEED_TCH_F, cbfn, data);
+ } while (1);
+}
+
+
/* we consciously ignore the type of the request here */
-void paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr,
- struct gsm_lchan *lchan)
+static void _paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr,
+ struct gsm_lchan *lchan)
{
struct gsm_bts_paging_state *bts_entry = &bts->paging;
struct gsm_paging_request *req, *req2;
@@ -256,6 +272,31 @@ void paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr,
}
}
+/* Stop paging on all other bts' */
+void paging_request_stop(struct gsm_bts *_bts, struct gsm_subscriber *subscr,
+ struct gsm_lchan *lchan)
+{
+ struct gsm_bts *bts = NULL;
+
+ _paging_request_stop(_bts, subscr, lchan);
+
+ do {
+ /*
+ * FIXME: Don't use the lac of the subscriber...
+ * as it might have magically changed the lac.. use the
+ * location area of the _bts as reconfiguration of the
+ * network is probably happening less often.
+ */
+ bts = gsm_bts_by_lac(_bts->network, subscr->lac, bts);
+ if (!bts)
+ break;
+
+ /* Stop paging */
+ if (bts != _bts)
+ _paging_request_stop(bts, subscr, NULL);
+ } while (1);
+}
+
void paging_update_buffer_space(struct gsm_bts *bts, u_int16_t free_slots)
{
bts->paging.available_slots = free_slots;
diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am
index 2b72c9c11..2d4e81c75 100644
--- a/openbsc/tests/Makefile.am
+++ b/openbsc/tests/Makefile.am
@@ -1 +1 @@
-SUBDIRS = debug timer sms gsm0408 db
+SUBDIRS = debug timer sms gsm0408 db channel
diff --git a/openbsc/tests/channel/Makefile.am b/openbsc/tests/channel/Makefile.am
new file mode 100644
index 000000000..60defe0a6
--- /dev/null
+++ b/openbsc/tests/channel/Makefile.am
@@ -0,0 +1,14 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+AM_CFLAGS=-Wall -ggdb3
+
+noinst_PROGRAMS = channel_test
+
+channel_test_SOURCES = channel_test.c \
+ $(top_srcdir)/src/db.c \
+ $(top_srcdir)/src/gsm_subscriber.c \
+ $(top_srcdir)/src/debug.c \
+ $(top_srcdir)/src/timer.c \
+ $(top_srcdir)/src/select.c \
+ $(top_srcdir)/src/gsm_data.c
+channel_test_LDADD = -ldl -ldbi
+
diff --git a/openbsc/tests/channel/channel_test.c b/openbsc/tests/channel/channel_test.c
new file mode 100644
index 000000000..1787e358c
--- /dev/null
+++ b/openbsc/tests/channel/channel_test.c
@@ -0,0 +1,72 @@
+/*
+ * (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 <stdio.h>
+
+#include <assert.h>
+
+#include <openbsc/select.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/abis_rsl.h>
+
+/* our handler */
+static int subscr_cb(unsigned int hook, unsigned int event, struct msgb *msg, void *data, void *param)
+{
+ assert(hook == 101);
+ assert(event == 200);
+ assert(msg == (void*)0x1323L);
+ assert(data == (void*)0x4242L);
+ assert(param == (void*)0x2342L);
+ printf("Reached, didn't crash, test passed\n");
+ return 0;
+}
+
+/* mock object for testing, directly invoke the cb... maybe later through the timer */
+void paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscriber, int type, gsm_cbfn *cbfn, void *data)
+{
+ cbfn(101, 200, (void*)0x1323L, (void*)0x4242L, data);
+}
+
+
+int main(int argc, char** argv)
+{
+ struct gsm_network network;
+
+ printf("Testing the gsm_subscriber chan logic\n");
+
+ /* Create a dummy network */
+ network.bts[0].location_area_code = 23;
+ network.bts[0].network = &network;
+
+ /* Create a dummy subscriber */
+ struct gsm_subscriber *subscr = subscr_alloc();
+ subscr->lac = 23;
+
+ /* Ask for a channel... */
+ subscr_get_channel(subscr, &network, RSL_CHANNEED_TCH_F, subscr_cb, (void*)0x2342L);
+
+ while (1) {
+ bsc_select_main(0);
+ }
+}
+
+void nm_state_event() {}
+void input_event() {}
+