gsmmap: Dump SYSTEM INFORMATION messages while processing
[osmocom-bb.git] / src / target / firmware / layer1 / prim_fbsb.c
1 /* Layer 1 - FCCH and SCH burst handling */
2
3 /* (C) 2010 by Harald Welte <laforge@gnumonks.org>
4  *
5  * All Rights Reserved
6  *
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.
11  *
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.
16  *
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.
20  *
21  */
22
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <errno.h>
28
29 #include <defines.h>
30 #include <debug.h>
31 #include <memory.h>
32 #include <byteorder.h>
33 #include <osmocom/gsm/gsm_utils.h>
34 #include <osmocom/core/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>
42
43 #include <layer1/sync.h>
44 #include <layer1/afc.h>
45 #include <layer1/toa.h>
46 #include <layer1/tdma_sched.h>
47 #include <layer1/mframe_sched.h>
48 #include <layer1/tpu_window.h>
49 #include <layer1/l23_api.h>
50
51 #include <l1ctl_proto.h>
52
53 #define FB0_RETRY_COUNT         3
54 #define AFC_RETRY_COUNT         30
55
56 extern uint16_t rf_arfcn; // TODO
57
58 struct mon_state {
59         uint32_t fnr_report;    /* frame number when DSP reported it */
60         int attempt;            /* which attempt was this ? */
61
62         int16_t toa;
63         uint16_t pm;
64         uint16_t angle;
65         uint16_t snr;
66
67         /* computed values */
68         int16_t freq_diff;
69
70         /* Sync Burst (SB) */
71         uint8_t bsic;
72         struct gsm_time time;
73 };
74
75 struct l1a_fb_state {
76         struct mon_state mon;
77         struct l1ctl_fbsb_req req;
78         int16_t initial_freq_err;
79         uint8_t fb_retries;
80         uint8_t afc_retries;
81 };
82
83 static struct l1a_fb_state fbs;
84 static struct mon_state *last_fb = &fbs.mon;
85
86 static void dump_mon_state(struct mon_state *fb)
87 {
88 #if 0
89         printf("(%u:%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz, "
90                 "SNR=%04x(%d.%u) OFFSET=%u SYNCHRO=%u\n",
91                 fb->fnr_report, fb->attempt, fb->toa,
92                 agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle),
93                 fb->snr, l1s_snr_int(fb->snr), l1s_snr_fract(fb->snr),
94                 tpu_get_offset(), tpu_get_synchro());
95 #else
96         printf("(%u:%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz\n",
97                 fb->fnr_report, fb->attempt, fb->toa,
98                 agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle));
99 #endif
100 }
101
102 static int l1ctl_fbsb_resp(uint8_t res)
103 {
104         struct msgb *msg;
105         struct l1ctl_fbsb_conf *resp;
106
107         msg = l1_create_l2_msg(L1CTL_FBSB_CONF, fbs.mon.time.fn,
108                                 l1s_snr_int(fbs.mon.snr),
109                                 fbs.req.band_arfcn);
110         if (!msg)
111                 return -ENOMEM;
112
113         resp = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*resp));
114         resp->initial_freq_err = htons(fbs.initial_freq_err);
115         resp->result = res;
116         resp->bsic = fbs.mon.bsic;
117
118         /* no need to set BSIC, as it is never used here */
119         l1_queue_for_l2(msg);
120
121         return 0;
122 }
123
124 /* SCH Burst Detection ********************************************************/
125
126 /* determine the GSM time and BSIC from a Sync Burst */
127 static uint8_t l1s_decode_sb(struct gsm_time *time, uint32_t sb)
128 {
129         uint8_t bsic = (sb >> 2) & 0x3f;
130         uint8_t t3p;
131
132         memset(time, 0, sizeof(*time));
133
134         /* TS 05.02 Chapter 3.3.2.2.1 SCH Frame Numbers */
135         time->t1 = ((sb >> 23) & 1) | ((sb >> 7) & 0x1fe) | ((sb << 9) & 0x600);
136         time->t2 = (sb >> 18) & 0x1f;
137         t3p = ((sb >> 24) & 1) | ((sb >> 15) & 6);
138         time->t3 = t3p*10 + 1;
139
140         /* TS 05.02 Chapter 4.3.3 TDMA frame number */
141         time->fn = gsm_gsmtime2fn(time);
142
143         time->tc = (time->fn / 51) % 8;
144
145         return bsic;
146 }
147
148 static void read_sb_result(struct mon_state *st, int attempt)
149 {
150         st->toa = dsp_api.db_r->a_serv_demod[D_TOA];
151         st->pm = dsp_api.db_r->a_serv_demod[D_PM]>>3;
152         st->angle = dsp_api.db_r->a_serv_demod[D_ANGLE];
153         st->snr = dsp_api.db_r->a_serv_demod[D_SNR];
154
155         st->freq_diff = ANGLE_TO_FREQ(st->angle);
156         st->fnr_report = l1s.current_time.fn;
157         st->attempt = attempt;
158
159         dump_mon_state(st);
160
161         if (st->snr > AFC_SNR_THRESHOLD)
162                 afc_input(st->freq_diff, rf_arfcn, 1);
163         else
164                 afc_input(st->freq_diff, rf_arfcn, 0);
165
166         dsp_api.r_page_used = 1;
167 }
168
169 /* Note: When we get the SB response, it is 2 TDMA frames after the SB
170  * actually happened, as it is a "C W W R" task */
171 #define SB2_LATENCY     2
172
173 static int l1s_sbdet_resp(__unused uint8_t p1, uint8_t attempt,
174                           __unused uint16_t p3)
175 {
176         uint32_t sb;
177         int qbits, fn_offset;
178         struct l1_cell_info *cinfo = &l1s.serving_cell;
179         int fnr_delta, bits_delta;
180         struct l1ctl_sync_new_ccch_resp *l1;
181         struct msgb *msg;
182
183         putchart('s');
184
185         if (dsp_api.db_r->a_sch[0] & (1<<B_SCH_CRC)) {
186                 /* mark READ page as being used */
187                 dsp_api.r_page_used = 1;
188
189                 /* after 2nd attempt, we failed */
190                 if (attempt == 2) {
191                         last_fb->attempt = 13;
192                         l1s_compl_sched(L1_COMPL_FB);
193                 }
194
195                 /* after 1st attempt, we simply wait for 2nd */
196                 return 0;
197         }
198
199         printf("SB%d ", attempt);
200         read_sb_result(last_fb, attempt);
201
202         sb = dsp_api.db_r->a_sch[3] | dsp_api.db_r->a_sch[4] << 16;
203         fbs.mon.bsic = l1s_decode_sb(&fbs.mon.time, sb);
204         printf("=> SB 0x%08x: BSIC=%u ", sb, fbs.mon.bsic);
205         l1s_time_dump(&fbs.mon.time);
206
207         l1s.serving_cell.bsic = fbs.mon.bsic;
208
209         /* calculate synchronisation value (TODO: only complete for qbits) */
210         last_fb->toa -= 23;
211         qbits = last_fb->toa * 4;
212         fn_offset = l1s.current_time.fn; // TODO
213
214         if (qbits > QBITS_PER_TDMA) {
215                 qbits -= QBITS_PER_TDMA;
216                 fn_offset -= 1;
217         } else if (qbits < 0)  {
218                 qbits += QBITS_PER_TDMA;
219                 fn_offset += 1;
220         }
221
222         fnr_delta = last_fb->fnr_report - attempt;
223         bits_delta = fnr_delta * BITS_PER_TDMA;
224
225         cinfo->fn_offset = fnr_delta;
226         cinfo->time_alignment = qbits;
227         cinfo->arfcn = rf_arfcn;
228
229         if (last_fb->toa > bits_delta)
230                 printf("=> DSP reports SB in bit that is %d bits in the "
231                         "future?!?\n", last_fb->toa - bits_delta);
232         else
233                 printf(" qbits=%u\n", qbits);
234
235         synchronize_tdma(&l1s.serving_cell);
236
237         /* if we have recived a SYNC burst, update our local GSM time */
238         gsm_fn2gsmtime(&l1s.current_time, fbs.mon.time.fn + SB2_LATENCY);
239         /* compute next time from new current time */
240         l1s.next_time = l1s.current_time;
241         l1s_time_inc(&l1s.next_time, 1);
242
243         /* If we call tdma_sched_reset(), which is only needed if there
244          * are further l1s_sbdet_resp() scheduled, we will bring
245          * dsp_api.db_r and dsp_api.db_w out of sync because we changed
246          * dsp_api.db_w for l1s_sbdet_cmd() and canceled
247          * l1s_sbdet_resp() which would change dsp_api.db_r. The DSP
248          * however expects dsp_api.db_w and dsp_api.db_r to be in sync
249          * (either "0 - 0" or "1 - 1"). So we have to bring dsp_api.db_w
250          * and dsp_api.db_r into sync again, otherwise NB reading will
251          * complain. We probably don't need the Abort command and could
252          * just bring dsp_api.db_w and dsp_api.db_r into sync.  */
253         if (attempt != 2) {
254                 tdma_sched_reset();
255                 l1s_dsp_abort();
256         }
257
258         l1s_reset_hw();
259         /* enable the MF Task for BCCH reading */
260         mframe_enable(MF_TASK_BCCH_NORM);
261         if (l1s.serving_cell.ccch_mode == CCCH_MODE_COMBINED)
262                 mframe_enable(MF_TASK_CCCH_COMB);
263         else if (l1s.serving_cell.ccch_mode == CCCH_MODE_NON_COMBINED)
264                 mframe_enable(MF_TASK_CCCH);
265
266         l1s_compl_sched(L1_COMPL_FB);
267
268         return 0;
269 }
270
271 static int l1s_sbdet_cmd(__unused uint8_t p1, __unused uint8_t p2,
272                          __unused uint16_t p3)
273 {
274         putchart('S');
275
276         fbs.mon.bsic = 0;
277         fbs.mon.time.fn = 0;
278
279         dsp_api.db_w->d_task_md = SB_DSP_TASK;
280         dsp_api.ndb->d_fb_mode = 0; /* wideband search */
281
282         /* Program TPU */
283         l1s_rx_win_ctrl(rf_arfcn, L1_RXWIN_SB, 0);
284
285         return 0;
286 }
287
288 /* This is how it is done by the TSM30 */
289 static const struct tdma_sched_item sb_sched_set[] = {
290         SCHED_ITEM_DT(l1s_sbdet_cmd, 0, 0, 1),  SCHED_END_FRAME(),
291         SCHED_ITEM_DT(l1s_sbdet_cmd, 0, 0, 2),  SCHED_END_FRAME(),
292                                                 SCHED_END_FRAME(),
293         SCHED_ITEM(l1s_sbdet_resp, -4, 0, 1),   SCHED_END_FRAME(),
294         SCHED_ITEM(l1s_sbdet_resp, -4, 0, 2),   SCHED_END_FRAME(),
295         SCHED_END_SET()
296 };
297
298 void l1s_sb_test(uint8_t base_fn)
299 {
300         tdma_schedule_set(base_fn, sb_sched_set, 0);
301 }
302 /* FCCH Burst *****************************************************************/
303
304 static int read_fb_result(struct mon_state *st, int attempt)
305 {
306         st->toa = dsp_api.ndb->a_sync_demod[D_TOA];
307         st->pm = dsp_api.ndb->a_sync_demod[D_PM]>>3;
308         st->angle = dsp_api.ndb->a_sync_demod[D_ANGLE];
309         st->snr = dsp_api.ndb->a_sync_demod[D_SNR];
310
311         //last_fb->angle = clip_int16(last_fb->angle, AFC_MAX_ANGLE);
312         st->freq_diff = ANGLE_TO_FREQ(last_fb->angle);
313         st->fnr_report = l1s.current_time.fn;
314         st->attempt = attempt;
315
316         dump_mon_state(st);
317
318         dsp_api.ndb->d_fb_det = 0;
319         dsp_api.ndb->a_sync_demod[D_TOA] = 0; /* TSM30 does it (really needed ?) */
320
321         /* Update AFC with current frequency offset */
322         afc_correct(st->freq_diff, rf_arfcn);
323
324         //tpu_dsp_frameirq_enable();
325         return 1;
326 }
327
328 static void fbinfo2cellinfo(struct l1_cell_info *cinfo,
329                             const struct mon_state *mon)
330 {
331         int ntdma, qbits, fn_offset, fnr_delta, bits_delta;
332
333         /* FIXME: where did this magic 23 come from? */
334         last_fb->toa -= 23;
335
336         if (last_fb->toa < 0) {
337                 qbits = (last_fb->toa + BITS_PER_TDMA) * 4;
338                 ntdma = -1;
339         } else {
340                 ntdma = (last_fb->toa) / BITS_PER_TDMA;
341                 qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4;
342         }
343
344         fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma;
345         fnr_delta = last_fb->fnr_report - last_fb->attempt;
346         bits_delta = fnr_delta * BITS_PER_TDMA;
347
348         cinfo->fn_offset = fnr_delta;
349         cinfo->time_alignment = qbits;
350         cinfo->arfcn = rf_arfcn;
351
352         if (last_fb->toa > bits_delta)
353                 printf("=> DSP reports FB in bit that is %d bits in "
354                         "the future?!?\n", last_fb->toa - bits_delta);
355         else {
356                 int fb_fnr = (last_fb->fnr_report - last_fb->attempt)
357                                 + last_fb->toa/BITS_PER_TDMA;
358                 printf("=>FB @ FNR %u fn_offset=%d qbits=%u\n",
359                         fb_fnr, fn_offset, qbits);
360         }
361 }
362
363 /* scheduler callback to issue a FB detection task to the DSP */
364 static int l1s_fbdet_cmd(__unused uint8_t p1, __unused uint8_t p2,
365                          uint16_t fb_mode)
366 {
367         if (fb_mode == 0) {
368                 putchart('F');
369         } else {
370                 putchart('V');
371         }
372
373         l1s.fb.mode = fb_mode;
374
375         /* Tell the RF frontend to set the gain appropriately */
376         rffe_compute_gain(-85, CAL_DSP_TGT_BB_LVL);
377
378         /* Program DSP */
379         dsp_api.db_w->d_task_md = FB_DSP_TASK;  /* maybe with I/Q swap? */
380         dsp_api.ndb->d_fb_mode = fb_mode;
381
382         /* Program TPU */
383         l1s_rx_win_ctrl(fbs.req.band_arfcn, L1_RXWIN_FB, 0);
384
385         return 0;
386 }
387
388 #if 0
389 #define FB0_SNR_THRESH  2000
390 #define FB1_SNR_THRESH  3000
391 #else
392 #define FB0_SNR_THRESH  0
393 #define FB1_SNR_THRESH  0
394 #endif
395
396 static const struct tdma_sched_item fb_sched_set[];
397
398 /* scheduler callback to check for a FB detection response */
399 static int l1s_fbdet_resp(__unused uint8_t p1, uint8_t attempt,
400                           uint16_t fb_mode)
401 {
402         putchart('f');
403
404         if (!dsp_api.ndb->d_fb_det) {
405                 /* we did not detect a FB */
406
407                 /* attempt < 12, do nothing */
408                 if (attempt < 12)
409                         return 0;
410
411                 /* attempt >= 12, we simply don't find one */
412
413                 /* If we don't reset here, we get DSP DMA errors */
414                 tdma_sched_reset();
415
416                 if (fbs.fb_retries < FB0_RETRY_COUNT) {
417                         /* retry once more */
418                         tdma_schedule_set(1, fb_sched_set, 0);
419                         fbs.fb_retries++;
420                 } else {
421                         last_fb->attempt = 13;
422                         l1s_compl_sched(L1_COMPL_FB);
423                 }
424
425                 return 0;
426         }
427
428         /* We found a frequency burst, reset everything */
429         l1s_reset_hw();
430
431         printf("FB%u ", dsp_api.ndb->d_fb_mode);
432         read_fb_result(last_fb, attempt);
433
434         /* if this is the first success, save freq err */
435         if (!fbs.initial_freq_err)
436                 fbs.initial_freq_err = last_fb->freq_diff;
437
438         /* If we don't reset here, we get DSP DMA errors */
439         tdma_sched_reset();
440
441         /* Immediately schedule further TDMA tasklets, if requested. Doing
442          * this directly from L1S means we can do this quickly without any
443          * additional delays */
444         if (fb_mode == 0) {
445                 if (fbs.req.flags & L1CTL_FBSB_F_FB1) {
446                         /* If we don't reset here, we get DSP DMA errors */
447                         tdma_sched_reset();
448                         /* FIXME: don't only use the last but an average */
449                         if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh1 &&
450                             last_fb->snr > FB0_SNR_THRESH) {
451                                 /* continue with FB1 task in DSP */
452                                 tdma_schedule_set(1, fb_sched_set, 1);
453                         } else {
454                                 if (fbs.afc_retries < AFC_RETRY_COUNT) {
455                                         tdma_schedule_set(1, fb_sched_set, 0);
456                                         fbs.afc_retries++;
457                                 } else {
458                                         /* Abort */
459                                         last_fb->attempt = 13;
460                                         l1s_compl_sched(L1_COMPL_FB);
461                                 }
462                         }
463                 } else
464                         l1s_compl_sched(L1_COMPL_FB);
465         } else if (fb_mode == 1) {
466                 if (fbs.req.flags & L1CTL_FBSB_F_SB) {
467
468         int ntdma, qbits;
469         /* FIXME: where did this magic 23 come from? */
470         last_fb->toa -= 23;
471
472         if (last_fb->toa < 0) {
473                 qbits = (last_fb->toa + BITS_PER_TDMA) * 4;
474                 ntdma = -1;
475         } else {
476                 ntdma = (last_fb->toa) / BITS_PER_TDMA;
477                 qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4;
478         }
479
480
481                         int fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma;
482                         int delay = fn_offset + 11 - l1s.current_time.fn - 1;
483                         printf("  fn_offset=%d (fn=%u + attempt=%u + ntdma = %d)\m",
484                                 fn_offset, l1s.current_time.fn, last_fb->attempt, ntdma);
485                         printf("  delay=%d (fn_offset=%d + 11 - fn=%u - 1\n", delay,
486                                 fn_offset, l1s.current_time.fn);
487                         printf("  scheduling next FB/SB detection task with delay %u\n", delay);
488                         if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh2 &&
489                             last_fb->snr > FB1_SNR_THRESH) {
490                                 /* synchronize before reading SB */
491                                 fbinfo2cellinfo(&l1s.serving_cell, last_fb);
492                                 synchronize_tdma(&l1s.serving_cell);
493                                 tdma_schedule_set(delay, sb_sched_set, 0);
494                         } else
495                                 tdma_schedule_set(delay, fb_sched_set, 1);
496                 } else
497                         l1s_compl_sched(L1_COMPL_FB);
498         }
499
500         return 0;
501 }
502
503 /* FB detection */
504 static const struct tdma_sched_item fb_sched_set[] = {
505         SCHED_ITEM_DT(l1s_fbdet_cmd, 0, 0, 0),  SCHED_END_FRAME(),
506                                                 SCHED_END_FRAME(),
507         SCHED_ITEM(l1s_fbdet_resp, -4, 0, 1),   SCHED_END_FRAME(),
508         SCHED_ITEM(l1s_fbdet_resp, -4, 0, 2),   SCHED_END_FRAME(),
509         SCHED_ITEM(l1s_fbdet_resp, -4, 0, 3),   SCHED_END_FRAME(),
510         SCHED_ITEM(l1s_fbdet_resp, -4, 0, 4),   SCHED_END_FRAME(),
511         SCHED_ITEM(l1s_fbdet_resp, -4, 0, 5),   SCHED_END_FRAME(),
512         SCHED_ITEM(l1s_fbdet_resp, -4, 0, 6),   SCHED_END_FRAME(),
513         SCHED_ITEM(l1s_fbdet_resp, -4, 0, 7),   SCHED_END_FRAME(),
514         SCHED_ITEM(l1s_fbdet_resp, -4, 0, 8),   SCHED_END_FRAME(),
515         SCHED_ITEM(l1s_fbdet_resp, -4, 0, 9),   SCHED_END_FRAME(),
516         SCHED_ITEM(l1s_fbdet_resp, -4, 0, 10),  SCHED_END_FRAME(),
517         SCHED_ITEM(l1s_fbdet_resp, -4, 0, 11),  SCHED_END_FRAME(),
518         SCHED_ITEM(l1s_fbdet_resp, -4, 0, 12),  SCHED_END_FRAME(),
519         SCHED_END_SET()
520 };
521
522 /* Asynchronous completion handler for FB detection */
523 static void l1a_fb_compl(__unused enum l1_compl c)
524 {
525         struct l1_cell_info *cinfo = &l1s.serving_cell;
526
527         if (last_fb->attempt >= 13) {
528                 /* FB detection failed, signal this via L1CTL */
529                 return l1ctl_fbsb_resp(255);
530         }
531
532         /* FIME: use l1s.neigh_cell[fbs.cinfo_idx] */
533         fbinfo2cellinfo(&l1s.serving_cell, last_fb);
534
535         /* send FBSB_CONF success message via L1CTL */
536         l1ctl_fbsb_resp(0);
537 }
538
539 void l1s_fbsb_req(uint8_t base_fn, struct l1ctl_fbsb_req *req)
540 {
541         /* copy + endian convert request data */
542         fbs.req.band_arfcn = ntohs(req->band_arfcn);
543         fbs.req.timeout = ntohs(req->timeout);
544         fbs.req.freq_err_thresh1 = ntohs(req->freq_err_thresh1);
545         fbs.req.freq_err_thresh2 = ntohs(req->freq_err_thresh2);
546         fbs.req.num_freqerr_avg = req->num_freqerr_avg;
547         fbs.req.flags = req->flags;
548         fbs.req.sync_info_idx = req->sync_info_idx;
549
550         /* clear initial frequency error */
551         fbs.initial_freq_err = 0;
552         fbs.fb_retries = 0;
553         fbs.afc_retries = 0;
554
555         /* Make sure we start at a 'center' AFCDAC output value */
556         afc_reset();
557
558         /* Reset the TOA loop counters */
559         toa_reset();
560
561         if (fbs.req.flags & L1CTL_FBSB_F_FB0)
562                 tdma_schedule_set(base_fn, fb_sched_set, 0);
563         else if (fbs.req.flags & L1CTL_FBSB_F_FB1)
564                 tdma_schedule_set(base_fn, fb_sched_set, 0);
565         else if (fbs.req.flags & L1CTL_FBSB_F_SB)
566                 tdma_schedule_set(base_fn, sb_sched_set, 0);
567
568 }
569
570 static __attribute__ ((constructor)) void l1s_prim_fbsb_init(void)
571 {
572         l1s.completion[L1_COMPL_FB] = &l1a_fb_compl;
573 }