fw/layer1: Add common TPU win setup/cleanup code
[osmocom-bb.git] / src / target / firmware / layer1 / prim_rx_nb.c
1 /* Layer 1 - Receiving Normal Bursts */
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
28 #include <defines.h>
29 #include <debug.h>
30 #include <memory.h>
31 #include <byteorder.h>
32 #include <osmocore/gsm_utils.h>
33 #include <osmocore/msgb.h>
34 #include <calypso/dsp_api.h>
35 #include <calypso/irq.h>
36 #include <calypso/tpu.h>
37 #include <calypso/tsp.h>
38 #include <calypso/dsp.h>
39 #include <calypso/timer.h>
40 #include <comm/sercomm.h>
41
42 #include <layer1/sync.h>
43 #include <layer1/afc.h>
44 #include <layer1/tdma_sched.h>
45 #include <layer1/mframe_sched.h>
46 #include <layer1/tpu_window.h>
47 #include <layer1/l23_api.h>
48 #include <layer1/rfch.h>
49
50 #include <l1ctl_proto.h>
51
52 struct l1s_rxnb_state {
53         struct l1s_meas_hdr meas[4];
54
55         struct msgb *msg;
56         struct l1ctl_info_dl *dl;
57         struct l1ctl_data_ind *di;
58 };
59
60 static struct l1s_rxnb_state rxnb;
61
62 static int l1s_nb_resp(__unused uint8_t p1, uint8_t burst_id, uint16_t p3)
63 {
64         struct gsm_time rx_time;
65         uint8_t mf_task_id = p3 & 0xff;
66         uint8_t mf_task_flags = p3 >> 8;
67         uint16_t rf_arfcn;
68         uint8_t tsc, tn;
69
70         putchart('n');
71
72         /* just for debugging, d_task_d should not be 0 */
73         if (dsp_api.db_r->d_task_d == 0) {
74                 puts("EMPTY\n");
75                 return 0;
76         }
77
78         /* DSP burst ID needs to corespond with what we expect */
79         if (dsp_api.db_r->d_burst_d != burst_id) {
80                 printf("BURST ID %u!=%u\n", dsp_api.db_r->d_burst_d, burst_id);
81                 return 0;
82         }
83
84         /* get radio parameters for _this_ burst */
85         gsm_fn2gsmtime(&rx_time, l1s.current_time.fn - 1);
86         rfch_get_params(&rx_time, &rf_arfcn, &tsc, &tn);
87
88         /* collect measurements */
89         rxnb.meas[burst_id].toa_qbit = dsp_api.db_r->a_serv_demod[D_TOA];
90         rxnb.meas[burst_id].pm_dbm8 =
91                 agc_inp_dbm8_by_pm(dsp_api.db_r->a_serv_demod[D_PM] >> 3);
92         rxnb.meas[burst_id].freq_err =
93                         ANGLE_TO_FREQ(dsp_api.db_r->a_serv_demod[D_ANGLE]);
94         rxnb.meas[burst_id].snr = dsp_api.db_r->a_serv_demod[D_SNR];
95
96         /* feed computed frequency error into AFC loop */
97         if (rxnb.meas[burst_id].snr > AFC_SNR_THRESHOLD)
98                 afc_input(rxnb.meas[burst_id].freq_err, rf_arfcn, 1);
99         else
100                 afc_input(rxnb.meas[burst_id].freq_err, rf_arfcn, 0);
101
102         /* Tell the RF frontend to set the gain appropriately */
103         rffe_set_gain(rxnb.meas[burst_id].pm_dbm8/8, CAL_DSP_TGT_BB_LVL);
104
105         /* 4th burst, get frame data */
106         if (dsp_api.db_r->d_burst_d == 3) {
107                 uint8_t i, j;
108                 uint16_t num_biterr;
109                 uint32_t avg_snr = 0;
110                 int32_t avg_dbm8 = 0;
111
112                 /* Get radio parameters for the first burst */
113                 gsm_fn2gsmtime(&rx_time, l1s.current_time.fn - 4);
114                 rfch_get_params(&rx_time, &rf_arfcn, &tsc, &tn);
115
116                 /* Set Channel Number depending on MFrame Task ID */
117                 rxnb.dl->chan_nr = mframe_task2chan_nr(mf_task_id, tn);
118
119                 /* Set SACCH indication in Link IDentifier */
120                 if (mf_task_flags & MF_F_SACCH)
121                         rxnb.dl->link_id = 0x40;
122                 else
123                         rxnb.dl->link_id = 0x00;
124
125                 rxnb.dl->band_arfcn = htons(rf_arfcn);
126
127                 rxnb.dl->frame_nr = htonl(rx_time.fn);
128
129                 /* compute average snr and rx level */
130                 for (i = 0; i < 4; ++i) {
131                         avg_snr += rxnb.meas[i].snr;
132                         avg_dbm8 += rxnb.meas[i].pm_dbm8;
133                 }
134                 rxnb.dl->snr = avg_snr / 4;
135                 rxnb.dl->rx_level = (avg_dbm8 / (8*4)) + 110;
136
137                 num_biterr = dsp_api.ndb->a_cd[2] & 0xffff;
138                 if (num_biterr > 0xff)
139                         rxnb.dl->num_biterr = 0xff;
140                 else
141                         rxnb.dl->num_biterr = num_biterr;
142
143                 rxnb.dl->fire_crc = ((dsp_api.ndb->a_cd[0] & 0xffff) & ((1 << B_FIRE1) | (1 << B_FIRE0))) >> B_FIRE0;
144
145                 /* copy actual data, skipping the information block [0,1,2] */
146                 for (j = 0,i = 3; i < 15; i++) {
147                         rxnb.di->data[j++] = dsp_api.ndb->a_cd[i] & 0xFF;
148                         rxnb.di->data[j++] = (dsp_api.ndb->a_cd[i] >> 8) & 0xFF;
149                 }
150
151                 l1_queue_for_l2(rxnb.msg);
152                 rxnb.msg = NULL; rxnb.dl = NULL; rxnb.di = NULL;
153
154                 /* clear downlink task */
155                 dsp_api.db_w->d_task_d = 0;
156         }
157
158         /* mark READ page as being used */
159         dsp_api.r_page_used = 1;
160
161         return 0;
162 }
163
164 static int l1s_nb_cmd(__unused uint8_t p1, uint8_t burst_id,
165                       __unused uint16_t p3)
166 {
167         uint16_t arfcn;
168         uint8_t tsc;
169
170         putchart('N');
171
172         if (burst_id == 1) {
173                 /* allocate message only at 2nd burst in case of
174                  * consecutive/overlapping normal burst RX tasks */
175                 /* FIXME: we actually want all allocation out of L1S! */
176                 if (rxnb.msg)
177                         printf("nb_cmd(0) and rxnb.msg != NULL\n");
178                 /* allocate msgb as needed. FIXME: from L1A ?? */
179                 rxnb.msg = l1ctl_msgb_alloc(L1CTL_DATA_IND);
180                 if (!rxnb.msg)
181                         printf("nb_cmd(0): unable to allocate msgb\n");
182                 rxnb.dl = (struct l1ctl_info_dl *) msgb_put(rxnb.msg, sizeof(*rxnb.dl));
183                 rxnb.di = (struct l1ctl_data_ind *) msgb_put(rxnb.msg, sizeof(*rxnb.di));
184         }
185
186         rfch_get_params(&l1s.next_time, &arfcn, &tsc, NULL);
187
188         dsp_load_rx_task(ALLC_DSP_TASK, burst_id, tsc);
189
190         l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB, 0);
191
192         return 0;
193 }
194
195 const struct tdma_sched_item nb_sched_set[] = {
196         SCHED_ITEM_DT(l1s_nb_cmd, 0, 0, 0),                                             SCHED_END_FRAME(),
197         SCHED_ITEM_DT(l1s_nb_cmd, 0, 0, 1),                                             SCHED_END_FRAME(),
198         SCHED_ITEM(l1s_nb_resp, -4, 0, 0),      SCHED_ITEM_DT(l1s_nb_cmd, 0, 0, 2),     SCHED_END_FRAME(),
199         SCHED_ITEM(l1s_nb_resp, -4, 0, 1),      SCHED_ITEM_DT(l1s_nb_cmd, 0, 0, 3),     SCHED_END_FRAME(),
200                                                 SCHED_ITEM(l1s_nb_resp, -4, 0, 2),      SCHED_END_FRAME(),
201                                                 SCHED_ITEM(l1s_nb_resp, -4, 0, 3),      SCHED_END_FRAME(),
202         SCHED_END_SET()
203 };