summaryrefslogtreecommitdiffstats
path: root/src/host/layer23/src/misc/bcch_scan.c
blob: 3ba3a1cd2effe3caededc782f2dbf458bc33cf39 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
/* BCCH Scanning code for OsmocomBB */

/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
 *
 * All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>

#include <l1ctl_proto.h>

#include <osmocom/core/logging.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/signal.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/gsm/rsl.h>

#include <osmocom/bb/common/l1ctl.h>
#include <osmocom/bb/common/osmocom_data.h>
#include <osmocom/bb/common/logging.h>

/* somewhere in 05.08 */
#define MAX_CELLS_IN_BA	32

/* Information about a single cell / BCCH */
struct cell_info {
	struct llist_head list;

	uint16_t band_arfcn;
	uint8_t bsic;
	uint8_t rxlev;

	struct {
		uint16_t mcc;	/* Mobile Country Code */
		uint16_t mnc;	/* Mobile Network Code */
		uint16_t lac;	/* Location Area Code */
		uint16_t rac;	/* Routing Area Code */
		uint16_t cid;	/* Cell ID */
	} id;
	uint16_t ba_arfcn[MAX_CELLS_IN_BA];
	uint8_t ba_arfcn_num;

	struct {
		int32_t fn_delta;	/* delta to current L1 fn */
		int16_t qbit_delta;
		int16_t afc_delta;
	} l1_sync;
};

#define AFS_F_PM_DONE	0x01
#define AFS_F_TESTED	0x02
#define AFS_F_BCCH	0x04
struct arfcn_state {
	uint8_t rxlev;
	uint8_t flags;
};

enum bscan_state {
	BSCAN_S_NONE,
	BSCAN_S_WAIT_DATA,
	BSCAN_S_DONE,
};

enum fps_state {
	FPS_S_NONE,
	FPS_S_PM_GSM900,
	FPS_S_PM_EGSM900,
	FPS_S_PM_GSM1800,
	FPS_S_BINFO,
};

struct full_power_scan {
	/* Full Power Scan */
	enum fps_state fps_state;
	struct arfcn_state arfcn_state[1024];

	struct osmocom_ms *ms;

	/* BCCH info part */
	enum bscan_state state;
	struct llist_head cell_list;
	struct cell_info *cur_cell;
	uint16_t cur_arfcn;
	struct osmo_timer_list timer;
};

static struct full_power_scan fps;

static int get_next_arfcn(struct full_power_scan *fps)
{
	unsigned int i;
	uint8_t best_rxlev = 0;
	int best_arfcn = -1;

	for (i = 0; i < ARRAY_SIZE(fps->arfcn_state); i++) {
		struct arfcn_state *af = &fps->arfcn_state[i];
		/* skip ARFCN's where we don't have a PM */
		if (!(af->flags & AFS_F_PM_DONE))
			continue;
		/* skip ARFCN's that we already tested */
		if (af->flags & AFS_F_TESTED)
			continue;
		/* if current arfcn_state is better than best so far,
		 * select it */
		if (af->rxlev > best_rxlev) {
			best_rxlev = af->rxlev;
			best_arfcn = i;
		}
	}
	printf("arfcn=%d rxlev=%u\n", best_arfcn, best_rxlev);
	return best_arfcn;
}

static struct cell_info *cell_info_alloc(void)
{
	struct cell_info *ci = talloc_zero(NULL, struct cell_info);
	return ci;
}

static void cell_info_free(struct cell_info *ci)
{
	talloc_free(ci);
}

/* start to scan for one ARFCN */
static int _cinfo_start_arfcn(unsigned int band_arfcn)
{
	int rc;

	/* ask L1 to try to tune to new ARFCN */
	/* FIXME: decode band */
	rc = l1ctl_tx_fbsb_req(fps.ms, band_arfcn,
	                       L1CTL_FBSB_F_FB01SB, 100, 0, CCCH_MODE_COMBINED,
			       fps.arfcn_state[band_arfcn].rxlev);
	if (rc < 0)
		return rc;

	/* allocate new cell info structure */
	fps.cur_cell = cell_info_alloc();
	fps.cur_arfcn = band_arfcn;
	fps.cur_cell->band_arfcn = band_arfcn;
	/* FIXME: start timer in case we never get a sync */
	fps.state = BSCAN_S_WAIT_DATA;
	osmo_timer_schedule(&fps.timer, 2, 0);

	return 0;
}


static void cinfo_next_cell(void *data)
{
	int rc;

	/* we've been waiting for BCCH info */
	fps.arfcn_state[fps.cur_arfcn].flags |= AFS_F_TESTED;
	/* if there is a BCCH, we need to add the collected BCCH
	 * information to our list */

	if (fps.arfcn_state[fps.cur_arfcn].flags & AFS_F_BCCH)
		llist_add(&fps.cur_cell->list, &fps.cell_list);
	else
		cell_info_free(fps.cur_cell);

	rc = get_next_arfcn(&fps);
	if (rc < 0) {
		fps.state = BSCAN_S_DONE;
		return;
	}
	/* start syncing to the next ARFCN */
	_cinfo_start_arfcn(rc);
}

static void cinfo_timer_cb(void *data)
{
	switch (fps.state) {
	case BSCAN_S_WAIT_DATA:
		cinfo_next_cell(data);
		break;
	}
}

/* Update cell_info for current cell with received BCCH info */
static int rx_bcch_info(const uint8_t *data)
{
	struct cell_info *ci = fps.cur_cell;
	struct gsm48_system_information_type_header *si_hdr;
	si_hdr = (struct gsm48_system_information_type_header *) data;;

	/* we definitely have a BCCH on this channel */
	fps.arfcn_state[ci->band_arfcn].flags |= AFS_F_BCCH;

	switch (si_hdr->system_information) {
	case GSM48_MT_RR_SYSINFO_1:
		/* FIXME: CA, RACH control */
		break;
	case GSM48_MT_RR_SYSINFO_2:
		/* FIXME: BA, NCC, RACH control */
		break;
	case GSM48_MT_RR_SYSINFO_3:
		/* FIXME: cell_id, LAI */
		break;
	case GSM48_MT_RR_SYSINFO_4:
		/* FIXME: LAI */
		break;
	}
	return 0;
}

/* Update L1/SCH information (AFC/QBIT/FN offset, BSIC) */
static int rx_sch_info()
{
	/* FIXME */
}

static int bscan_sig_cb(unsigned int subsys, unsigned int signal,
		     void *handler_data, void *signal_data)
{
	struct cell_info *ci = fps.cur_cell;
	struct osmocom_ms *ms;
	struct osmobb_meas_res *mr;
	uint16_t arfcn;
	int rc;

	if (subsys != SS_L1CTL)
		return 0;

	switch (signal) {
	case S_L1CTL_PM_RES:
		mr = signal_data;
		/* check if PM result is for same MS */
		if (fps.ms != mr->ms)
			return 0;
		arfcn = mr->band_arfcn & 0x3ff;
		/* update RxLev and notice that PM was done */
		fps.arfcn_state[arfcn].rxlev = mr->rx_lev;
		fps.arfcn_state[arfcn].flags |= AFS_F_PM_DONE;
		break;
	case S_L1CTL_PM_DONE:
		ms = signal_data;
		switch (fps.fps_state) {
		case FPS_S_PM_GSM900:
			fps.fps_state = FPS_S_PM_EGSM900;
			return l1ctl_tx_pm_req_range(ms, 955, 1023);
		case FPS_S_PM_EGSM900:
			fps.fps_state = FPS_S_PM_GSM1800;
			return l1ctl_tx_pm_req_range(ms, 512, 885);
		case FPS_S_PM_GSM1800:
			/* power measurement has finished, we can start
			 * to actually iterate over the ARFCN's and try
			 * to sync to BCCHs */
			fps.fps_state = FPS_S_BINFO;
			rc = get_next_arfcn(&fps);
			if (rc < 0) {
				fps.state = BSCAN_S_DONE;
				return 0;
			}
			_cinfo_start_arfcn(rc);
			break;
		}
		break;
	case S_L1CTL_FBSB_RESP:
		/* We actually got a FCCH/SCH burst */
#if 0
		fps.arfcn_state[ci->band_arfcn].flags |= AFS_F_BCCH;
		/* fallthrough */
#else
		break;
#endif
	case S_L1CTL_FBSB_ERR:
		/* We timed out, move on */
		if (fps.state == BSCAN_S_WAIT_DATA)
			cinfo_next_cell(NULL);
		break;
	}
	return 0;
}

/* start the full power scan */
int fps_start(struct osmocom_ms *ms)
{
	memset(&fps, 0, sizeof(fps));
	fps.ms = ms;

	fps.timer.cb = cinfo_timer_cb;
	fps.timer.data = &fps;

	/* Start by scanning the good old GSM900 band */
	fps.fps_state = FPS_S_PM_GSM900;
	return l1ctl_tx_pm_req_range(ms, 0, 124);
}

int fps_init(void)
{
	return osmo_signal_register_handler(SS_L1CTL, &bscan_sig_cb, NULL);
}