1 /* GSM Multiframe Scheduler Implementation (on top of TDMA sched) */
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.
29 #include <osmocom/gsm/gsm_utils.h>
31 #include <layer1/prim.h>
32 #include <layer1/sync.h>
33 #include <layer1/tdma_sched.h>
34 #include <layer1/mframe_sched.h>
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 */
42 /* At which number inside the modulo shall we be scheduled */
44 /* bit-mask of flags */
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 #define NEIGH_PM neigh_pm_sched_set
56 static const struct mframe_sched_item mf_bcch_norm[] = {
57 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 2 },
62 static const struct mframe_sched_item mf_bcch_ext[] = {
63 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 6 },
67 /* Full CCCH in a pure BCCH + CCCH C0T0 */
68 static const struct mframe_sched_item mf_ccch[] = {
69 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 6 },
70 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 12 },
71 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 16 },
72 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 22 },
73 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 26 },
74 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 32 },
75 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 36 },
76 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 42 },
77 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 46 },
81 /* Full CCCH in a combined CCCH on C0T0 */
82 static const struct mframe_sched_item mf_ccch_comb[] = {
83 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 6 },
84 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 12 },
85 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 16 },
89 /* SDCCH/4 in a combined CCCH on C0T0, cannot be FH */
90 static const struct mframe_sched_item mf_sdcch4_0[] = {
91 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 22 },
92 { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 22+15 },
93 { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 42,
94 .flags = MF_F_SACCH },
95 { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 42+15,
96 .flags = MF_F_SACCH },
99 static const struct mframe_sched_item mf_sdcch4_1[] = {
100 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 26 },
101 { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 26+15 },
102 { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 46,
103 .flags = MF_F_SACCH },
104 { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 46+15,
105 .flags = MF_F_SACCH },
106 { .sched_set = NULL }
108 static const struct mframe_sched_item mf_sdcch4_2[] = {
109 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 32 },
110 { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 32+15 },
111 { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 51+42,
112 .flags = MF_F_SACCH },
113 { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 51+42+15,
114 .flags = MF_F_SACCH },
115 { .sched_set = NULL }
117 static const struct mframe_sched_item mf_sdcch4_3[] = {
118 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 36 },
119 { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 36+15 },
120 { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 51+46,
121 .flags = MF_F_SACCH },
122 { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 51+46+15,
123 .flags = MF_F_SACCH },
124 { .sched_set = NULL }
127 /* SDCCH/8, can be frequency hopping (FH) */
128 static const struct mframe_sched_item mf_sdcch8_0[] = {
129 { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 0 },
130 { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 0+15 },
131 { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 32,
132 .flags = MF_F_SACCH },
133 { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 32+15,
134 .flags = MF_F_SACCH },
135 { .sched_set = NULL }
137 static const struct mframe_sched_item mf_sdcch8_1[] = {
138 { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 4 },
139 { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 4+15 },
140 { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 36,
141 .flags = MF_F_SACCH },
142 { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 36+15,
143 .flags = MF_F_SACCH },
144 { .sched_set = NULL }
146 static const struct mframe_sched_item mf_sdcch8_2[] = {
147 { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 8 },
148 { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 8+15 },
149 { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 40,
150 .flags = MF_F_SACCH },
151 { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 40+15,
152 .flags = MF_F_SACCH },
153 { .sched_set = NULL }
155 static const struct mframe_sched_item mf_sdcch8_3[] = {
156 { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 12 },
157 { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 12+15 },
158 { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 44,
159 .flags = MF_F_SACCH },
160 { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 44+15,
161 .flags = MF_F_SACCH },
162 { .sched_set = NULL }
164 static const struct mframe_sched_item mf_sdcch8_4[] = {
165 { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 16 },
166 { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 16+15 },
167 { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+32,
168 .flags = MF_F_SACCH },
169 { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+32+15,
170 .flags = MF_F_SACCH },
171 { .sched_set = NULL }
173 static const struct mframe_sched_item mf_sdcch8_5[] = {
174 { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 20 },
175 { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 20+15 },
176 { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+36,
177 .flags = MF_F_SACCH },
178 { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+36+15,
179 .flags = MF_F_SACCH },
180 { .sched_set = NULL }
182 static const struct mframe_sched_item mf_sdcch8_6[] = {
183 { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 24 },
184 { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 24+15 },
185 { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+40,
186 .flags = MF_F_SACCH },
187 { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+40+15,
188 .flags = MF_F_SACCH },
189 { .sched_set = NULL }
191 static const struct mframe_sched_item mf_sdcch8_7[] = {
192 { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 28 },
193 { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 28+15 },
194 { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+44,
195 .flags = MF_F_SACCH },
196 { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+44+15,
197 .flags = MF_F_SACCH },
198 { .sched_set = NULL }
201 /* Measurement for MF 51 */
202 static const struct mframe_sched_item mf_neigh_pm51[] = {
203 { .sched_set = NEIGH_PM , .modulo = 51, .frame_nr = 50 },
204 { .sched_set = NULL }
208 #define TCH tch_sched_set
209 #define TCH_A tch_a_sched_set
210 #define TCH_D tch_d_sched_set
212 static const struct mframe_sched_item mf_tch_f_even[] = {
213 { .sched_set = TCH, .modulo = 13, .frame_nr = 0 },
214 { .sched_set = TCH, .modulo = 13, .frame_nr = 1 },
215 { .sched_set = TCH, .modulo = 13, .frame_nr = 2 },
216 { .sched_set = TCH, .modulo = 13, .frame_nr = 3 },
217 { .sched_set = TCH, .modulo = 13, .frame_nr = 4 },
218 { .sched_set = TCH, .modulo = 13, .frame_nr = 5 },
219 { .sched_set = TCH, .modulo = 13, .frame_nr = 6 },
220 { .sched_set = TCH, .modulo = 13, .frame_nr = 7 },
221 { .sched_set = TCH, .modulo = 13, .frame_nr = 8 },
222 { .sched_set = TCH, .modulo = 13, .frame_nr = 9 },
223 { .sched_set = TCH, .modulo = 13, .frame_nr = 10 },
224 { .sched_set = TCH, .modulo = 13, .frame_nr = 11 },
225 { .sched_set = TCH_A, .modulo = 26, .frame_nr = 12,
226 .flags = MF_F_SACCH },
227 { .sched_set = NULL }
230 static const struct mframe_sched_item mf_tch_f_odd[] = {
231 { .sched_set = TCH, .modulo = 13, .frame_nr = 0 },
232 { .sched_set = TCH, .modulo = 13, .frame_nr = 1 },
233 { .sched_set = TCH, .modulo = 13, .frame_nr = 2 },
234 { .sched_set = TCH, .modulo = 13, .frame_nr = 3 },
235 { .sched_set = TCH, .modulo = 13, .frame_nr = 4 },
236 { .sched_set = TCH, .modulo = 13, .frame_nr = 5 },
237 { .sched_set = TCH, .modulo = 13, .frame_nr = 6 },
238 { .sched_set = TCH, .modulo = 13, .frame_nr = 7 },
239 { .sched_set = TCH, .modulo = 13, .frame_nr = 8 },
240 { .sched_set = TCH, .modulo = 13, .frame_nr = 9 },
241 { .sched_set = TCH, .modulo = 13, .frame_nr = 10 },
242 { .sched_set = TCH, .modulo = 13, .frame_nr = 11 },
243 { .sched_set = TCH_A, .modulo = 26, .frame_nr = 25,
244 .flags = MF_F_SACCH },
245 { .sched_set = NULL }
248 static const struct mframe_sched_item mf_tch_h_0[] = {
249 { .sched_set = TCH, .modulo = 13, .frame_nr = 0 },
250 { .sched_set = TCH_D, .modulo = 13, .frame_nr = 1 },
251 { .sched_set = TCH, .modulo = 13, .frame_nr = 2 },
252 { .sched_set = TCH_D, .modulo = 13, .frame_nr = 3 },
253 { .sched_set = TCH, .modulo = 13, .frame_nr = 4 },
254 { .sched_set = TCH_D, .modulo = 13, .frame_nr = 5 },
255 { .sched_set = TCH, .modulo = 13, .frame_nr = 6 },
256 { .sched_set = TCH_D, .modulo = 13, .frame_nr = 7 },
257 { .sched_set = TCH, .modulo = 13, .frame_nr = 8 },
258 { .sched_set = TCH_D, .modulo = 13, .frame_nr = 9 },
259 { .sched_set = TCH, .modulo = 13, .frame_nr = 10 },
260 { .sched_set = TCH_D, .modulo = 13, .frame_nr = 11 },
261 { .sched_set = TCH_A, .modulo = 26, .frame_nr = 12,
262 .flags = MF_F_SACCH },
263 { .sched_set = NULL }
266 static const struct mframe_sched_item mf_tch_h_1[] = {
267 { .sched_set = TCH_D, .modulo = 13, .frame_nr = 0 },
268 { .sched_set = TCH, .modulo = 13, .frame_nr = 1 },
269 { .sched_set = TCH_D, .modulo = 13, .frame_nr = 2 },
270 { .sched_set = TCH, .modulo = 13, .frame_nr = 3 },
271 { .sched_set = TCH_D, .modulo = 13, .frame_nr = 4 },
272 { .sched_set = TCH, .modulo = 13, .frame_nr = 5 },
273 { .sched_set = TCH_D, .modulo = 13, .frame_nr = 6 },
274 { .sched_set = TCH, .modulo = 13, .frame_nr = 7 },
275 { .sched_set = TCH_D, .modulo = 13, .frame_nr = 8 },
276 { .sched_set = TCH, .modulo = 13, .frame_nr = 9 },
277 { .sched_set = TCH_D, .modulo = 13, .frame_nr = 10 },
278 { .sched_set = TCH, .modulo = 13, .frame_nr = 11 },
279 { .sched_set = TCH_A, .modulo = 26, .frame_nr = 25,
280 .flags = MF_F_SACCH },
281 { .sched_set = NULL }
284 /* Measurement for MF 26 */
285 static const struct mframe_sched_item mf_neigh_pm26_even[] = {
286 { .sched_set = NEIGH_PM , .modulo = 26, .frame_nr = 25 },
287 { .sched_set = NULL }
289 static const struct mframe_sched_item mf_neigh_pm26_odd[] = {
290 { .sched_set = NEIGH_PM , .modulo = 26, .frame_nr = 12 },
291 { .sched_set = NULL }
295 static const struct mframe_sched_item mf_tx_all_nb[] = {
296 { .sched_set = NB_QUAD_FH_UL, .modulo = 4, .frame_nr = 0 },
297 { .sched_set = NULL }
300 static const struct mframe_sched_item *sched_set_for_task[32] = {
301 [MF_TASK_BCCH_NORM] = mf_bcch_norm,
302 [MF_TASK_BCCH_EXT] = mf_bcch_ext,
303 [MF_TASK_CCCH] = mf_ccch,
304 [MF_TASK_CCCH_COMB] = mf_ccch_comb,
306 [MF_TASK_SDCCH4_0] = mf_sdcch4_0,
307 [MF_TASK_SDCCH4_1] = mf_sdcch4_1,
308 [MF_TASK_SDCCH4_2] = mf_sdcch4_2,
309 [MF_TASK_SDCCH4_3] = mf_sdcch4_3,
311 [MF_TASK_SDCCH8_0] = mf_sdcch8_0,
312 [MF_TASK_SDCCH8_1] = mf_sdcch8_1,
313 [MF_TASK_SDCCH8_2] = mf_sdcch8_2,
314 [MF_TASK_SDCCH8_3] = mf_sdcch8_3,
315 [MF_TASK_SDCCH8_4] = mf_sdcch8_4,
316 [MF_TASK_SDCCH8_5] = mf_sdcch8_5,
317 [MF_TASK_SDCCH8_6] = mf_sdcch8_6,
318 [MF_TASK_SDCCH8_7] = mf_sdcch8_7,
320 [MF_TASK_TCH_F_EVEN] = mf_tch_f_even,
321 [MF_TASK_TCH_F_ODD] = mf_tch_f_odd,
322 [MF_TASK_TCH_H_0] = mf_tch_h_0,
323 [MF_TASK_TCH_H_1] = mf_tch_h_1,
325 [MF_TASK_NEIGH_PM51] = mf_neigh_pm51,
326 [MF_TASK_NEIGH_PM26E] = mf_neigh_pm26_even,
327 [MF_TASK_NEIGH_PM26O] = mf_neigh_pm26_odd,
329 [MF_TASK_UL_ALL_NB] = mf_tx_all_nb,
332 /* encodes a channel number according to 08.58 Chapter 9.3.1 */
333 uint8_t mframe_task2chan_nr(enum mframe_task mft, uint8_t ts)
338 case MF_TASK_BCCH_NORM:
339 case MF_TASK_BCCH_EXT:
343 case MF_TASK_CCCH_COMB:
346 case MF_TASK_SDCCH4_0:
349 case MF_TASK_SDCCH4_1:
352 case MF_TASK_SDCCH4_2:
355 case MF_TASK_SDCCH4_3:
358 case MF_TASK_SDCCH8_0:
361 case MF_TASK_SDCCH8_1:
364 case MF_TASK_SDCCH8_2:
367 case MF_TASK_SDCCH8_3:
370 case MF_TASK_SDCCH8_4:
373 case MF_TASK_SDCCH8_5:
376 case MF_TASK_SDCCH8_6:
379 case MF_TASK_SDCCH8_7:
382 case MF_TASK_TCH_F_EVEN:
383 case MF_TASK_TCH_F_ODD:
386 case MF_TASK_TCH_H_0:
389 case MF_TASK_TCH_H_1:
392 case MF_TASK_UL_ALL_NB:
393 /* ERROR: cannot express as channel number */
398 return (cbits << 3) | (ts & 0x7);
401 /* how many TDMA frame ticks should we schedule events ahead? */
402 #define SCHEDULE_AHEAD 2
404 /* how long do we need to tell the DSP in advance what we want to do? */
405 #define SCHEDULE_LATENCY 1
407 /* (test and) schedule one particular sched_item_set by means of the TDMA scheduler */
408 static void mframe_schedule_set(enum mframe_task task_id)
410 const struct mframe_sched_item *set = sched_set_for_task[task_id];
411 const struct mframe_sched_item *si;
413 for (si = set; si->sched_set != NULL; si++) {
414 unsigned int trigger = si->frame_nr % si->modulo;
415 unsigned int current = (l1s.current_time.fn + SCHEDULE_AHEAD) % si->modulo;
416 if (current == trigger) {
420 /* Schedule the set */
421 /* FIXME: what to do with SACCH Flag etc? */
422 rv = tdma_schedule_set(SCHEDULE_AHEAD-SCHEDULE_LATENCY,
423 si->sched_set, task_id | (si->flags<<8));
425 /* Compute the next safe time to queue a DSP command */
426 fn = l1s.current_time.fn;
427 ADD_MODULO(fn, rv - 2, GSM_MAX_FN); /* -2 = worst case last dsp command */
428 if ((fn > l1s.mframe_sched.safe_fn) ||
429 (l1s.mframe_sched.safe_fn >= GSM_MAX_FN))
430 l1s.mframe_sched.safe_fn = fn;
435 /* Enable a specific task */
436 void mframe_enable(enum mframe_task task_id)
438 l1s.mframe_sched.tasks_tgt |= (1 << task_id);
441 /* Disable a specific task */
442 void mframe_disable(enum mframe_task task_id)
444 l1s.mframe_sched.tasks_tgt &= ~(1 << task_id);
447 /* Replace the current active set by the new one */
448 void mframe_set(uint32_t tasks)
450 l1s.mframe_sched.tasks_tgt = tasks;
453 /* Schedule mframe_sched_items according to current MF TASK list */
454 void mframe_schedule(void)
459 /* Try to enable/disable task to meet target bitmap */
460 fn_diff = l1s.mframe_sched.safe_fn - l1s.current_time.fn;
461 if ((fn_diff <= 0) || (fn_diff >= (GSM_MAX_FN>>1)) ||
462 (l1s.mframe_sched.safe_fn >= GSM_MAX_FN))
463 /* If nothing is in the way, enable new tasks */
464 l1s.mframe_sched.tasks = l1s.mframe_sched.tasks_tgt;
466 /* Else, Disable only */
467 l1s.mframe_sched.tasks &= l1s.mframe_sched.tasks_tgt;
469 /* Schedule any active pending set */
470 for (i = 0; i < 32; i++) {
471 if (l1s.mframe_sched.tasks & (1 << i))
472 mframe_schedule_set(i);
476 /* reset the scheduler, disabling all tasks */
477 void mframe_reset(void)
479 l1s.mframe_sched.tasks = 0;
480 l1s.mframe_sched.tasks_tgt = 0;
481 l1s.mframe_sched.safe_fn = -1UL; /* Force safe */