1 /* Layer 1 - FCCH and SCH burst handling */
3 /* (C) 2010 by Harald Welte <laforge@gnumonks.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
32 #include <byteorder.h>
33 #include <osmocore/gsm_utils.h>
34 #include <osmocore/msgb.h>
35 #include <calypso/dsp_api.h>
36 #include <calypso/irq.h>
37 #include <calypso/tpu.h>
38 #include <calypso/tsp.h>
39 #include <calypso/dsp.h>
40 #include <calypso/timer.h>
41 #include <comm/sercomm.h>
43 #include <layer1/sync.h>
44 #include <layer1/afc.h>
45 #include <layer1/tdma_sched.h>
46 #include <layer1/mframe_sched.h>
47 #include <layer1/tpu_window.h>
48 #include <layer1/l23_api.h>
50 #include <l1a_l23_interface.h>
52 #define FB0_RETRY_COUNT 3
53 #define AFC_RETRY_COUNT 30
55 extern uint16_t rf_arfcn; // TODO
58 uint32_t fnr_report; /* frame number when DSP reported it */
59 int attempt; /* which attempt was this ? */
76 struct l1ctl_fbsb_req req;
77 int16_t initial_freq_err;
82 static struct l1a_fb_state fbs;
83 static struct mon_state *last_fb = &fbs.mon;
85 static void dump_mon_state(struct mon_state *fb)
88 printf("(%u:%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz, "
89 "SNR=%04x(%d.%u) OFFSET=%u SYNCHRO=%u\n",
90 fb->fnr_report, fb->attempt, fb->toa,
91 agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle),
92 fb->snr, l1s_snr_int(fb->snr), l1s_snr_fract(fb->snr),
93 tpu_get_offset(), tpu_get_synchro());
95 printf("(%u:%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz\n",
96 fb->fnr_report, fb->attempt, fb->toa,
97 agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle));
101 static int l1ctl_fbsb_resp(uint8_t res)
104 struct l1ctl_fbsb_resp *resp;
106 msg = l1_create_l2_msg(L1CTL_FBSB_RESP, fbs.mon.time.fn,
107 l1s_snr_int(fbs.mon.snr),
112 resp = (struct l1ctl_fbsb_resp *) msgb_put(msg, sizeof(*resp));
113 resp->initial_freq_err = htons(fbs.initial_freq_err);
115 resp->bsic = fbs.mon.bsic;
117 /* no need to set BSIC, as it is never used here */
118 l1_queue_for_l2(msg);
123 /* SCH Burst Detection ********************************************************/
125 /* determine the GSM time and BSIC from a Sync Burst */
126 static uint8_t l1s_decode_sb(struct gsm_time *time, uint32_t sb)
128 uint8_t bsic = (sb >> 2) & 0x3f;
131 memset(time, 0, sizeof(*time));
133 /* TS 05.02 Chapter 3.3.2.2.1 SCH Frame Numbers */
134 time->t1 = ((sb >> 23) & 1) | ((sb >> 7) & 0x1fe) | ((sb << 9) & 0x600);
135 time->t2 = (sb >> 18) & 0x1f;
136 t3p = ((sb >> 24) & 1) | ((sb >> 15) & 6);
137 time->t3 = t3p*10 + 1;
139 /* TS 05.02 Chapter 4.3.3 TDMA frame number */
140 time->fn = gsm_gsmtime2fn(time);
142 time->tc = (time->fn / 51) % 8;
147 static void read_sb_result(struct mon_state *st, int attempt)
149 st->toa = dsp_api.db_r->a_serv_demod[D_TOA];
150 st->pm = dsp_api.db_r->a_serv_demod[D_PM]>>3;
151 st->angle = dsp_api.db_r->a_serv_demod[D_ANGLE];
152 st->snr = dsp_api.db_r->a_serv_demod[D_SNR];
154 st->freq_diff = ANGLE_TO_FREQ(st->angle);
155 st->fnr_report = l1s.current_time.fn;
156 st->attempt = attempt;
160 if (st->snr > AFC_SNR_THRESHOLD)
161 afc_input(st->freq_diff, rf_arfcn, 1);
163 afc_input(st->freq_diff, rf_arfcn, 0);
165 dsp_api.r_page_used = 1;
168 /* Note: When we get the SB response, it is 2 TDMA frames after the SB
169 * actually happened, as it is a "C W W R" task */
170 #define SB2_LATENCY 2
172 static int l1s_sbdet_resp(__unused uint8_t p1, uint8_t attempt,
173 __unused uint16_t p3)
176 int qbits, fn_offset;
177 struct l1_cell_info *cinfo = &l1s.serving_cell;
178 int fnr_delta, bits_delta;
179 struct l1ctl_sync_new_ccch_resp *l1;
184 if (dsp_api.db_r->a_sch[0] & (1<<B_SCH_CRC)) {
185 /* mark READ page as being used */
186 dsp_api.r_page_used = 1;
188 /* after 2nd attempt, we failed */
190 last_fb->attempt = 13;
191 l1s_compl_sched(L1_COMPL_FB);
194 /* after 1st attempt, we simply wait for 2nd */
198 printf("SB%d ", attempt);
199 read_sb_result(last_fb, attempt);
201 sb = dsp_api.db_r->a_sch[3] | dsp_api.db_r->a_sch[4] << 16;
202 fbs.mon.bsic = l1s_decode_sb(&fbs.mon.time, sb);
203 printf("=> SB 0x%08x: BSIC=%u ", sb, fbs.mon.bsic);
204 l1s_time_dump(&fbs.mon.time);
206 l1s.serving_cell.bsic = fbs.mon.bsic;
208 /* calculate synchronisation value (TODO: only complete for qbits) */
210 qbits = last_fb->toa * 4;
211 fn_offset = l1s.current_time.fn; // TODO
213 if (qbits > QBITS_PER_TDMA) {
214 qbits -= QBITS_PER_TDMA;
216 } else if (qbits < 0) {
217 qbits += QBITS_PER_TDMA;
221 fnr_delta = last_fb->fnr_report - attempt;
222 bits_delta = fnr_delta * BITS_PER_TDMA;
224 cinfo->fn_offset = fnr_delta;
225 cinfo->time_alignment = qbits;
226 cinfo->arfcn = rf_arfcn;
228 if (last_fb->toa > bits_delta)
229 printf("=> DSP reports SB in bit that is %d bits in the "
230 "future?!?\n", last_fb->toa - bits_delta);
232 printf(" qbits=%u\n", qbits);
234 synchronize_tdma(&l1s.serving_cell);
236 /* if we have recived a SYNC burst, update our local GSM time */
237 gsm_fn2gsmtime(&l1s.current_time, fbs.mon.time.fn + SB2_LATENCY);
238 /* compute next time from new current time */
239 l1s.next_time = l1s.current_time;
240 l1s_time_inc(&l1s.next_time, 1);
242 /* If we call tdma_sched_reset(), which is only needed if there
243 * are further l1s_sbdet_resp() scheduled, we will bring
244 * dsp_api.db_r and dsp_api.db_w out of sync because we changed
245 * dsp_api.db_w for l1s_sbdet_cmd() and canceled
246 * l1s_sbdet_resp() which would change dsp_api.db_r. The DSP
247 * however expects dsp_api.db_w and dsp_api.db_r to be in sync
248 * (either "0 - 0" or "1 - 1"). So we have to bring dsp_api.db_w
249 * and dsp_api.db_r into sync again, otherwise NB reading will
250 * complain. We probably don't need the Abort command and could
251 * just bring dsp_api.db_w and dsp_api.db_r into sync. */
258 /* enable the MF Task for BCCH reading */
259 mframe_enable(MF_TASK_BCCH_NORM);
260 mframe_enable(MF_TASK_CCCH_COMB);
262 l1s_compl_sched(L1_COMPL_FB);
267 static int l1s_sbdet_cmd(__unused uint8_t p1, __unused uint8_t p2,
268 __unused uint16_t p3)
275 dsp_api.db_w->d_task_md = SB_DSP_TASK;
276 dsp_api.ndb->d_fb_mode = 0; /* wideband search */
280 l1s_rx_win_ctrl(rf_arfcn, L1_RXWIN_SB);
286 /* This is how it is done by the TSM30 */
287 static const struct tdma_sched_item sb_sched_set[] = {
288 SCHED_ITEM(l1s_sbdet_cmd, 0, 1), SCHED_END_FRAME(),
289 SCHED_ITEM(l1s_sbdet_cmd, 0, 2), SCHED_END_FRAME(),
291 SCHED_ITEM(l1s_sbdet_resp, 0, 1), SCHED_END_FRAME(),
292 SCHED_ITEM(l1s_sbdet_resp, 0, 2), SCHED_END_FRAME(),
296 void l1s_sb_test(uint8_t base_fn)
298 tdma_schedule_set(base_fn, sb_sched_set, 0);
300 /* FCCH Burst *****************************************************************/
302 static int read_fb_result(struct mon_state *st, int attempt)
304 st->toa = dsp_api.ndb->a_sync_demod[D_TOA];
305 st->pm = dsp_api.ndb->a_sync_demod[D_PM]>>3;
306 st->angle = dsp_api.ndb->a_sync_demod[D_ANGLE];
307 st->snr = dsp_api.ndb->a_sync_demod[D_SNR];
309 //last_fb->angle = clip_int16(last_fb->angle, AFC_MAX_ANGLE);
310 st->freq_diff = ANGLE_TO_FREQ(last_fb->angle);
311 st->fnr_report = l1s.current_time.fn;
312 st->attempt = attempt;
316 dsp_api.ndb->d_fb_det = 0;
317 dsp_api.ndb->a_sync_demod[D_TOA] = 0; /* TSM30 does it (really needed ?) */
319 /* Update AFC with current frequency offset */
320 afc_correct(st->freq_diff, rf_arfcn);
322 //tpu_dsp_frameirq_enable();
326 static void fbinfo2cellinfo(struct l1_cell_info *cinfo,
327 const struct mon_state *mon)
329 int ntdma, qbits, fn_offset, fnr_delta, bits_delta;
331 /* FIXME: where did this magic 23 come from? */
334 if (last_fb->toa < 0) {
335 qbits = (last_fb->toa + BITS_PER_TDMA) * 4;
338 ntdma = (last_fb->toa) / BITS_PER_TDMA;
339 qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4;
342 fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma;
343 fnr_delta = last_fb->fnr_report - last_fb->attempt;
344 bits_delta = fnr_delta * BITS_PER_TDMA;
346 cinfo->fn_offset = fnr_delta;
347 cinfo->time_alignment = qbits;
348 cinfo->arfcn = rf_arfcn;
350 if (last_fb->toa > bits_delta)
351 printf("=> DSP reports FB in bit that is %d bits in "
352 "the future?!?\n", last_fb->toa - bits_delta);
354 int fb_fnr = (last_fb->fnr_report - last_fb->attempt)
355 + last_fb->toa/BITS_PER_TDMA;
356 printf("=>FB @ FNR %u fn_offset=%d qbits=%u\n",
357 fb_fnr, fn_offset, qbits);
361 /* scheduler callback to issue a FB detection task to the DSP */
362 static int l1s_fbdet_cmd(__unused uint8_t p1, __unused uint8_t p2,
371 l1s.fb.mode = fb_mode;
374 dsp_api.db_w->d_task_md = FB_DSP_TASK; /* maybe with I/Q swap? */
375 dsp_api.ndb->d_fb_mode = fb_mode;
379 l1s_rx_win_ctrl(fbs.req.band_arfcn, L1_RXWIN_FB);
386 #define FB0_SNR_THRESH 2000
387 #define FB1_SNR_THRESH 3000
389 #define FB0_SNR_THRESH 0
390 #define FB1_SNR_THRESH 0
393 static const struct tdma_sched_item fb_sched_set[];
395 /* scheduler callback to check for a FB detection response */
396 static int l1s_fbdet_resp(__unused uint8_t p1, uint8_t attempt,
401 if (!dsp_api.ndb->d_fb_det) {
402 /* we did not detect a FB */
404 /* attempt < 12, do nothing */
408 /* attempt >= 12, we simply don't find one */
410 /* If we don't reset here, we get DSP DMA errors */
413 if (fbs.fb_retries < FB0_RETRY_COUNT) {
414 /* retry once more */
415 tdma_schedule_set(1, fb_sched_set, 0);
418 last_fb->attempt = 13;
419 l1s_compl_sched(L1_COMPL_FB);
425 /* We found a frequency burst, reset everything */
428 printf("FB%u ", dsp_api.ndb->d_fb_mode);
429 read_fb_result(last_fb, attempt);
431 /* if this is the first success, save freq err */
432 if (!fbs.initial_freq_err)
433 fbs.initial_freq_err = last_fb->freq_diff;
435 /* If we don't reset here, we get DSP DMA errors */
438 /* Immediately schedule further TDMA tasklets, if requested. Doing
439 * this directly from L1S means we can do this quickly without any
440 * additional delays */
442 if (fbs.req.flags & L1CTL_FBSB_F_FB1) {
443 /* If we don't reset here, we get DSP DMA errors */
445 /* FIXME: don't only use the last but an average */
446 if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh1 &&
447 last_fb->snr > FB0_SNR_THRESH) {
448 /* continue with FB1 task in DSP */
449 tdma_schedule_set(1, fb_sched_set, 1);
451 if (fbs.afc_retries < AFC_RETRY_COUNT) {
452 tdma_schedule_set(1, fb_sched_set, 0);
456 last_fb->attempt = 13;
457 l1s_compl_sched(L1_COMPL_FB);
461 l1s_compl_sched(L1_COMPL_FB);
462 } else if (fb_mode == 1) {
463 if (fbs.req.flags & L1CTL_FBSB_F_SB) {
466 /* FIXME: where did this magic 23 come from? */
469 if (last_fb->toa < 0) {
470 qbits = (last_fb->toa + BITS_PER_TDMA) * 4;
473 ntdma = (last_fb->toa) / BITS_PER_TDMA;
474 qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4;
478 int fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma;
479 int delay = fn_offset + 11 - l1s.current_time.fn - 1;
480 printf(" fn_offset=%d (fn=%u + attempt=%u + ntdma = %d)\m",
481 fn_offset, l1s.current_time.fn, last_fb->attempt, ntdma);
482 printf(" delay=%d (fn_offset=%d + 11 - fn=%u - 1\n", delay,
483 fn_offset, l1s.current_time.fn);
484 printf(" scheduling next FB/SB detection task with delay %u\n", delay);
485 if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh2 &&
486 last_fb->snr > FB1_SNR_THRESH) {
487 /* synchronize before reading SB */
488 fbinfo2cellinfo(&l1s.serving_cell, last_fb);
489 synchronize_tdma(&l1s.serving_cell);
490 tdma_schedule_set(delay, sb_sched_set, 0);
492 tdma_schedule_set(delay, fb_sched_set, 1);
494 l1s_compl_sched(L1_COMPL_FB);
501 static const struct tdma_sched_item fb_sched_set[] = {
502 SCHED_ITEM(l1s_fbdet_cmd, 0, 0), SCHED_END_FRAME(),
504 SCHED_ITEM(l1s_fbdet_resp, 0, 1), SCHED_END_FRAME(),
505 SCHED_ITEM(l1s_fbdet_resp, 0, 2), SCHED_END_FRAME(),
506 SCHED_ITEM(l1s_fbdet_resp, 0, 3), SCHED_END_FRAME(),
507 SCHED_ITEM(l1s_fbdet_resp, 0, 4), SCHED_END_FRAME(),
508 SCHED_ITEM(l1s_fbdet_resp, 0, 5), SCHED_END_FRAME(),
509 SCHED_ITEM(l1s_fbdet_resp, 0, 6), SCHED_END_FRAME(),
510 SCHED_ITEM(l1s_fbdet_resp, 0, 7), SCHED_END_FRAME(),
511 SCHED_ITEM(l1s_fbdet_resp, 0, 8), SCHED_END_FRAME(),
512 SCHED_ITEM(l1s_fbdet_resp, 0, 9), SCHED_END_FRAME(),
513 SCHED_ITEM(l1s_fbdet_resp, 0, 10), SCHED_END_FRAME(),
514 SCHED_ITEM(l1s_fbdet_resp, 0, 11), SCHED_END_FRAME(),
515 SCHED_ITEM(l1s_fbdet_resp, 0, 12), SCHED_END_FRAME(),
519 /* Asynchronous completion handler for FB detection */
520 static void l1a_fb_compl(__unused enum l1_compl c)
522 struct l1_cell_info *cinfo = &l1s.serving_cell;
524 if (last_fb->attempt >= 13) {
525 /* FB detection failed, signal this via L1CTL */
526 return l1ctl_fbsb_resp(255);
529 /* FIME: use l1s.neigh_cell[fbs.cinfo_idx] */
530 fbinfo2cellinfo(&l1s.serving_cell, last_fb);
532 /* send FBSB_RESP success message via L1CTL */
536 void l1s_fbsb_req(uint8_t base_fn, struct l1ctl_fbsb_req *req)
538 /* copy + endian convert request data */
539 fbs.req.band_arfcn = ntohs(req->band_arfcn);
540 fbs.req.timeout = ntohs(req->timeout);
541 fbs.req.freq_err_thresh1 = ntohs(req->freq_err_thresh1);
542 fbs.req.freq_err_thresh2 = ntohs(req->freq_err_thresh2);
543 fbs.req.num_freqerr_avg = req->num_freqerr_avg;
544 fbs.req.flags = req->flags;
545 fbs.req.sync_info_idx = req->sync_info_idx;
547 /* clear initial frequency error */
548 fbs.initial_freq_err = 0;
551 /* Make sure we start at a 'center' AFCDAC output value */
554 if (fbs.req.flags & L1CTL_FBSB_F_FB0)
555 tdma_schedule_set(base_fn, fb_sched_set, 0);
556 else if (fbs.req.flags & L1CTL_FBSB_F_FB1)
557 tdma_schedule_set(base_fn, fb_sched_set, 0);
558 else if (fbs.req.flags & L1CTL_FBSB_F_SB)
559 tdma_schedule_set(base_fn, sb_sched_set, 0);
561 l1s.completion[L1_COMPL_FB] = &l1a_fb_compl;