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 <osmocore/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
55 static const struct mframe_sched_item mf_bcch_norm[] = {
56 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 2 },
61 static const struct mframe_sched_item mf_bcch_ext[] = {
62 { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 6 },
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 },
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 },
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 },
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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,
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,
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,
225 [MF_TASK_UL_ALL_NB] = mf_tx_all_nb,
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)
234 case MF_TASK_BCCH_NORM:
235 case MF_TASK_BCCH_EXT:
239 case MF_TASK_CCCH_COMB:
242 case MF_TASK_SDCCH4_0:
245 case MF_TASK_SDCCH4_1:
248 case MF_TASK_SDCCH4_2:
251 case MF_TASK_SDCCH4_3:
254 case MF_TASK_SDCCH8_0:
257 case MF_TASK_SDCCH8_1:
260 case MF_TASK_SDCCH8_2:
263 case MF_TASK_SDCCH8_3:
266 case MF_TASK_SDCCH8_4:
269 case MF_TASK_SDCCH8_5:
272 case MF_TASK_SDCCH8_6:
275 case MF_TASK_SDCCH8_7:
278 case MF_TASK_UL_ALL_NB:
279 /* ERROR: cannot express as channel number */
284 return (cbits << 3) | (ts & 0x7);
287 /* how many TDMA frame ticks should we schedule events ahead? */
288 #define SCHEDULE_AHEAD 2
290 /* how long do we need to tell the DSP in advance what we want to do? */
291 #define SCHEDULE_LATENCY 1
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)
296 const struct mframe_sched_item *set = sched_set_for_task[task_id];
297 const struct mframe_sched_item *si;
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) {
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));
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;
321 /* Enable a specific task */
322 void mframe_enable(enum mframe_task task_id)
324 l1s.mframe_sched.tasks_tgt |= (1 << task_id);
327 /* Disable a specific task */
328 void mframe_disable(enum mframe_task task_id)
330 l1s.mframe_sched.tasks_tgt &= ~(1 << task_id);
333 /* Replace the current active set by the new one */
334 void mframe_set(uint32_t tasks)
336 l1s.mframe_sched.tasks_tgt = tasks;
339 /* Schedule mframe_sched_items according to current MF TASK list */
340 void mframe_schedule(void)
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;
352 /* Else, Disable only */
353 l1s.mframe_sched.tasks &= l1s.mframe_sched.tasks_tgt;
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);
362 /* reset the scheduler, disabling all tasks */
363 void mframe_reset(void)
365 l1s.mframe_sched.tasks = 0;
366 l1s.mframe_sched.tasks_tgt = 0;
367 l1s.mframe_sched.safe_fn = -1UL; /* Force safe */