8e92b93c8ce60a961f50b2483a2c5672b3b9109e
[osmocom-bb.git] / src / target / firmware / layer1 / mframe_sched.c
1 /* GSM Multiframe Scheduler Implementation (on top of TDMA sched) */
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
27 #include <debug.h>
28
29 #include <osmocore/gsm_utils.h>
30
31 #include <layer1/prim.h>
32 #include <layer1/sync.h>
33 #include <layer1/tdma_sched.h>
34 #include <layer1/mframe_sched.h>
35
36 /* A multiframe operation which can be scheduled for a multiframe */
37 struct mframe_sched_item {
38         /* The TDMA scheduler item that shall be scheduled */
39         const struct tdma_sched_item *sched_set;
40         /* Which modulo shall be used on the frame number */
41         uint16_t modulo;
42         /* At which number inside the modulo shall we be scheduled */
43         uint16_t frame_nr;
44         /* bit-mask of flags */
45         uint16_t flags;
46 };
47
48 /* FIXME: properly clean this up */
49 #define NB_QUAD_DL      nb_sched_set
50 #define NB_QUAD_FH_DL   NB_QUAD_DL
51 #define NB_QUAD_UL      nb_sched_set_ul
52 #define NB_QUAD_FH_UL   NB_QUAD_UL
53
54 /* BCCH Normal */
55 static const struct mframe_sched_item mf_bcch_norm[] = {
56         { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 2 },
57         { .sched_set = NULL }
58 };
59
60 /* BCCH Extended */
61 static const struct mframe_sched_item mf_bcch_ext[] = {
62         { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 6 },
63         { .sched_set = NULL }
64 };
65
66 /* Full CCCH in a pure BCCH + CCCH C0T0 */
67 static const struct mframe_sched_item mf_ccch[] = {
68         { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 6 },
69         { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 12 },
70         { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 16 },
71         { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 22 },
72         { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 26 },
73         { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 32 },
74         { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 36 },
75         { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 42 },
76         { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 46 },
77         { .sched_set = NULL }
78 };
79
80 /* Full CCCH in a combined CCCH on C0T0 */
81 static const struct mframe_sched_item mf_ccch_comb[] = {
82         { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 6 },
83         { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 12 },
84         { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 16 },
85         { .sched_set = NULL }
86 };
87
88 /* SDCCH/4 in a combined CCCH on C0T0, cannot be FH */
89 static const struct mframe_sched_item mf_sdcch4_0[] = {
90         { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 22 },
91         { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 22+15 },
92         { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 42,
93           .flags = MF_F_SACCH },
94         { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 42+15,
95           .flags = MF_F_SACCH },
96         { .sched_set = NULL }
97 };
98 static const struct mframe_sched_item mf_sdcch4_1[] = {
99         { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 26 },
100         { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 26+15 },
101         { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 46,
102           .flags = MF_F_SACCH },
103         { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 46+15,
104           .flags = MF_F_SACCH },
105         { .sched_set = NULL }
106 };
107 static const struct mframe_sched_item mf_sdcch4_2[] = {
108         { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 32 },
109         { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 32+15 },
110         { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 51+42,
111           .flags = MF_F_SACCH },
112         { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 51+42+15,
113           .flags = MF_F_SACCH },
114         { .sched_set = NULL }
115 };
116 static const struct mframe_sched_item mf_sdcch4_3[] = {
117         { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 36 },
118         { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 36+15 },
119         { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 51+46,
120           .flags = MF_F_SACCH },
121         { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 51+46+15,
122           .flags = MF_F_SACCH },
123         { .sched_set = NULL }
124 };
125
126 /* SDCCH/8, can be frequency hopping (FH) */
127 static const struct mframe_sched_item mf_sdcch8_0[] = {
128         { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 0 },
129         { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 0+15 },
130         { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 32,
131           .flags = MF_F_SACCH },
132         { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 32+15,
133           .flags = MF_F_SACCH },
134         { .sched_set = NULL }
135 };
136 static const struct mframe_sched_item mf_sdcch8_1[] = {
137         { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 4 },
138         { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 4+15 },
139         { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 36,
140           .flags = MF_F_SACCH },
141         { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 36+15,
142           .flags = MF_F_SACCH },
143         { .sched_set = NULL }
144 };
145 static const struct mframe_sched_item mf_sdcch8_2[] = {
146         { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 8 },
147         { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 8+15 },
148         { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 40,
149           .flags = MF_F_SACCH },
150         { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 40+15,
151           .flags = MF_F_SACCH },
152         { .sched_set = NULL }
153 };
154 static const struct mframe_sched_item mf_sdcch8_3[] = {
155         { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 12 },
156         { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 12+15 },
157         { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 44,
158           .flags = MF_F_SACCH },
159         { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 44+15,
160           .flags = MF_F_SACCH },
161         { .sched_set = NULL }
162 };
163 static const struct mframe_sched_item mf_sdcch8_4[] = {
164         { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 16 },
165         { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 16+15 },
166         { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+32,
167           .flags = MF_F_SACCH },
168         { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+32+15,
169           .flags = MF_F_SACCH },
170         { .sched_set = NULL }
171 };
172 static const struct mframe_sched_item mf_sdcch8_5[] = {
173         { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 20 },
174         { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 20+15 },
175         { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+36,
176           .flags = MF_F_SACCH },
177         { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+36+15,
178           .flags = MF_F_SACCH },
179         { .sched_set = NULL }
180 };
181 static const struct mframe_sched_item mf_sdcch8_6[] = {
182         { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 24 },
183         { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 24+15 },
184         { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+40,
185           .flags = MF_F_SACCH },
186         { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+40+15,
187           .flags = MF_F_SACCH },
188         { .sched_set = NULL }
189 };
190 static const struct mframe_sched_item mf_sdcch8_7[] = {
191         { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 28 },
192         { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 28+15 },
193         { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+44,
194           .flags = MF_F_SACCH },
195         { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+44+15,
196           .flags = MF_F_SACCH },
197         { .sched_set = NULL }
198 };
199
200 static const struct mframe_sched_item mf_tx_all_nb[] = {
201         { .sched_set = NB_QUAD_FH_UL, .modulo = 4, .frame_nr = 0 },
202         { .sched_set = NULL }
203 };
204
205 static const struct mframe_sched_item *sched_set_for_task[32] = {
206         [MF_TASK_BCCH_NORM] = mf_bcch_norm,
207         [MF_TASK_BCCH_EXT] = mf_bcch_ext,
208         [MF_TASK_CCCH] = mf_ccch,
209         [MF_TASK_CCCH_COMB] = mf_ccch_comb,
210
211         [MF_TASK_SDCCH4_0] = mf_sdcch4_0,
212         [MF_TASK_SDCCH4_1] = mf_sdcch4_1,
213         [MF_TASK_SDCCH4_2] = mf_sdcch4_2,
214         [MF_TASK_SDCCH4_3] = mf_sdcch4_3,
215
216         [MF_TASK_SDCCH8_0] = mf_sdcch8_0,
217         [MF_TASK_SDCCH8_1] = mf_sdcch8_1,
218         [MF_TASK_SDCCH8_2] = mf_sdcch8_2,
219         [MF_TASK_SDCCH8_3] = mf_sdcch8_3,
220         [MF_TASK_SDCCH8_4] = mf_sdcch8_4,
221         [MF_TASK_SDCCH8_5] = mf_sdcch8_5,
222         [MF_TASK_SDCCH8_6] = mf_sdcch8_6,
223         [MF_TASK_SDCCH8_7] = mf_sdcch8_7,
224
225         [MF_TASK_UL_ALL_NB] = mf_tx_all_nb,
226 };
227
228 /* encodes a channel number according to 08.58 Chapter 9.3.1 */
229 uint8_t mframe_task2chan_nr(enum mframe_task mft, uint8_t ts)
230 {
231         uint8_t cbits;
232
233         switch (mft) {
234         case MF_TASK_BCCH_NORM:
235         case MF_TASK_BCCH_EXT:
236                 cbits = 0x10;
237                 break;
238         case MF_TASK_CCCH:
239         case MF_TASK_CCCH_COMB:
240                 cbits = 0x12;
241                 break;
242         case MF_TASK_SDCCH4_0:
243                 cbits = 0x04 + 0;
244                 break;
245         case MF_TASK_SDCCH4_1:
246                 cbits = 0x04 + 1;
247                 break;
248         case MF_TASK_SDCCH4_2:
249                 cbits = 0x04 + 2;
250                 break;
251         case MF_TASK_SDCCH4_3:
252                 cbits = 0x04 + 3;
253                 break;
254         case MF_TASK_SDCCH8_0:
255                 cbits = 0x08 + 0;
256                 break;
257         case MF_TASK_SDCCH8_1:
258                 cbits = 0x08 + 1;
259                 break;
260         case MF_TASK_SDCCH8_2:
261                 cbits = 0x08 + 2;
262                 break;
263         case MF_TASK_SDCCH8_3:
264                 cbits = 0x08 + 3;
265                 break;
266         case MF_TASK_SDCCH8_4:
267                 cbits = 0x08 + 4;
268                 break;
269         case MF_TASK_SDCCH8_5:
270                 cbits = 0x08 + 5;
271                 break;
272         case MF_TASK_SDCCH8_6:
273                 cbits = 0x08 + 6;
274                 break;
275         case MF_TASK_SDCCH8_7:
276                 cbits = 0x08 + 7;
277                 break;
278         case MF_TASK_UL_ALL_NB:
279                 /* ERROR: cannot express as channel number */
280                 cbits = 0;
281                 break;
282         }
283
284         return (cbits << 3) | (ts & 0x7);
285 }
286
287 /* how many TDMA frame ticks should we schedule events ahead? */
288 #define SCHEDULE_AHEAD  2
289
290 /* how long do we need to tell the DSP in advance what we want to do? */
291 #define SCHEDULE_LATENCY        1
292
293 /* (test and) schedule one particular sched_item_set by means of the TDMA scheduler */
294 static void mframe_schedule_set(enum mframe_task task_id)
295 {
296         const struct mframe_sched_item *set = sched_set_for_task[task_id];
297         const struct mframe_sched_item *si;
298
299         for (si = set; si->sched_set != NULL; si++) {
300                 unsigned int trigger = si->frame_nr % si->modulo;
301                 unsigned int current = (l1s.current_time.fn + SCHEDULE_AHEAD) % si->modulo;
302                 if (current == trigger) {
303                         uint32_t fn;
304                         int rv;
305
306                         /* Schedule the set */
307                         /* FIXME: what to do with SACCH Flag etc? */
308                         rv = tdma_schedule_set(SCHEDULE_AHEAD-SCHEDULE_LATENCY,
309                                           si->sched_set, task_id | (si->flags<<8));
310
311                         /* Compute the next safe time to queue a DSP command */
312                         fn = l1s.current_time.fn;
313                         ADD_MODULO(fn, rv - 2, GSM_MAX_FN); /* -2 = worst case last dsp command */
314                         if ((fn > l1s.mframe_sched.safe_fn) ||
315                             (l1s.mframe_sched.safe_fn >= GSM_MAX_FN))
316                                 l1s.mframe_sched.safe_fn = fn;
317                 }
318         }
319 }
320
321 /* Enable a specific task */
322 void mframe_enable(enum mframe_task task_id)
323 {
324         l1s.mframe_sched.tasks_tgt |= (1 << task_id);
325 }
326
327 /* Disable a specific task */
328 void mframe_disable(enum mframe_task task_id)
329 {
330         l1s.mframe_sched.tasks_tgt &= ~(1 << task_id);
331 }
332
333 /* Replace the current active set by the new one */
334 void mframe_set(uint32_t tasks)
335 {
336         l1s.mframe_sched.tasks_tgt = tasks;
337 }
338
339 /* Schedule mframe_sched_items according to current MF TASK list */
340 void mframe_schedule(void)
341 {
342         unsigned int i;
343         int fn_diff;
344
345         /* Try to enable/disable task to meet target bitmap */
346         fn_diff = l1s.mframe_sched.safe_fn - l1s.current_time.fn;
347         if ((fn_diff <= 0) || (fn_diff >= (GSM_MAX_FN>>1)) ||
348             (l1s.mframe_sched.safe_fn >= GSM_MAX_FN))
349                 /* If nothing is in the way, enable new tasks */
350                 l1s.mframe_sched.tasks = l1s.mframe_sched.tasks_tgt;
351         else
352                 /* Else, Disable only */
353                 l1s.mframe_sched.tasks &= l1s.mframe_sched.tasks_tgt;
354
355         /* Schedule any active pending set */
356         for (i = 0; i < 32; i++) {
357                 if (l1s.mframe_sched.tasks & (1 << i))
358                         mframe_schedule_set(i);
359         }
360 }
361
362 /* reset the scheduler, disabling all tasks */
363 void mframe_reset(void)
364 {
365         l1s.mframe_sched.tasks = 0;
366         l1s.mframe_sched.tasks_tgt = 0;
367         l1s.mframe_sched.safe_fn = -1UL;        /* Force safe */
368 }
369