2 * OsmocomBB <-> SDR connection bridge
3 * TDMA scheduler: primitive management
5 * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 #include <osmocom/core/msgb.h>
31 #include <osmocom/core/logging.h>
32 #include <osmocom/core/linuxlist.h>
34 #include <osmocom/gsm/protocol/gsm_04_08.h>
35 #include <osmocom/gsm/protocol/gsm_08_58.h>
37 #include "scheduler.h"
38 #include "sched_trx.h"
43 * Initializes a new primitive by allocating memory
44 * and filling some meta-information (e.g. lchan type).
46 * @param trx TRX instance to be used as initial talloc context
47 * @param prim external prim pointer (will point to the allocated prim)
48 * @param pl_len prim payload length
49 * @param chan_nr RSL channel description (used to set a proper chan)
50 * @param link_id RSL link description (used to set a proper chan)
51 * @return zero in case of success, otherwise a error number
53 int sched_prim_init(struct trx_instance
*trx
,
54 struct trx_ts_prim
**prim
, size_t pl_len
,
55 uint8_t chan_nr
, uint8_t link_id
)
57 enum trx_lchan_type lchan_type
;
58 struct trx_ts_prim
*new_prim
;
61 /* Determine lchan type */
62 lchan_type
= sched_trx_chan_nr2lchan_type(chan_nr
, link_id
);
64 LOGP(DSCH
, LOGL_ERROR
, "Couldn't determine lchan type "
65 "for chan_nr=%02x and link_id=%02x\n", chan_nr
, link_id
);
69 /* How much memory do we need? */
70 len
= sizeof(struct trx_ts_prim
); /* Primitive header */
71 len
+= pl_len
; /* Requested payload size */
73 /* Allocate a new primitive */
74 new_prim
= talloc_zero_size(trx
, len
);
75 if (new_prim
== NULL
) {
76 LOGP(DSCH
, LOGL_ERROR
, "Failed to allocate memory\n");
80 /* Init primitive header */
81 new_prim
->payload_len
= pl_len
;
82 new_prim
->chan
= lchan_type
;
84 /* Set external pointer */
91 * Adds a primitive to the end of transmit queue of a particular
92 * timeslot, whose index is parsed from chan_nr.
94 * @param trx TRX instance
95 * @param prim to be enqueued primitive
96 * @param chan_nr RSL channel description
97 * @return zero in case of success, otherwise a error number
99 int sched_prim_push(struct trx_instance
*trx
,
100 struct trx_ts_prim
*prim
, uint8_t chan_nr
)
105 /* Determine TS index */
108 LOGP(DSCH
, LOGL_ERROR
, "Incorrect TS index %u\n", tn
);
112 /* Check whether required timeslot is allocated and configured */
113 ts
= trx
->ts_list
[tn
];
114 if (ts
== NULL
|| ts
->mf_layout
== NULL
) {
115 LOGP(DSCH
, LOGL_ERROR
, "Timeslot %u isn't configured\n", tn
);
120 * Change talloc context of primitive
121 * from trx to the parent ts
123 talloc_steal(ts
, prim
);
125 /* Add primitive to TS transmit queue */
126 llist_add_tail(&prim
->list
, &ts
->tx_prims
);
132 * Dequeues a TCH or FACCH frame, prioritizing the second.
133 * In case if a FACCH frame is found, a TCH frame is being
134 * dropped (i.e. replaced).
136 * @param queue a transmit queue to take a prim from
137 * @return a FACCH or TCH primitive, otherwise NULL
139 static struct trx_ts_prim
*sched_prim_dequeue_tch(struct llist_head
*queue
)
141 struct trx_ts_prim
*facch
= NULL
;
142 struct trx_ts_prim
*tch
= NULL
;
143 struct trx_ts_prim
*i
;
145 /* Attempt to find a pair of FACCH and TCH frames */
146 llist_for_each_entry(i
, queue
, list
) {
147 /* Find one FACCH frame */
148 if (!facch
&& PRIM_IS_FACCH(i
))
151 /* Find one TCH frame */
152 if (!tch
&& PRIM_IS_TCH(i
))
155 /* If both are found */
160 /* Prioritize FACCH */
162 /* We found a pair, dequeue both */
163 llist_del(&facch
->list
);
164 llist_del(&tch
->list
);
169 /* FACCH replaces TCH */
172 /* Only FACCH was found */
173 llist_del(&facch
->list
);
176 /* Only TCH was found */
177 llist_del(&tch
->list
);
183 * e.g. only SACCH frames are in queue
189 * Dequeues a single primitive of required type
190 * from a specified transmit queue.
192 * @param queue a transmit queue to take a prim from
193 * @param lchan_type required primitive type
194 * @return a primitive or NULL if not found
196 struct trx_ts_prim
*sched_prim_dequeue(struct llist_head
*queue
,
197 enum trx_lchan_type lchan_type
)
199 struct trx_ts_prim
*prim
;
201 /* There is nothing to dequeue */
202 if (llist_empty(queue
))
205 /* TCH requires FACCH prioritization, so handle it separately */
206 if (CHAN_IS_TCH(lchan_type
))
207 return sched_prim_dequeue_tch(queue
);
209 llist_for_each_entry(prim
, queue
, list
) {
210 if (prim
->chan
== lchan_type
) {
211 llist_del(&prim
->list
);
220 * Drops the current primitive of specified logical channel
222 * @param lchan a logical channel to drop prim from
224 void sched_prim_drop(struct trx_lchan_state
*lchan
)
226 /* Forget this primitive */
227 talloc_free(lchan
->prim
);
232 * Assigns a dummy primitive to a lchan depending on its type.
233 * Could be used when there is nothing to transmit, but
234 * CBTX (Continuous Burst Transmission) is assumed.
236 * @param lchan lchan to assign a primitive
237 * @return zero in case of success, otherwise a error code
239 int sched_prim_dummy(struct trx_lchan_state
*lchan
)
241 enum trx_lchan_type chan
= lchan
->type
;
242 uint8_t tch_mode
= lchan
->tch_mode
;
243 struct trx_ts_prim
*prim
;
244 uint8_t prim_buffer
[40];
249 * TS 144.006, section 8.4.2.3 "Fill frames"
250 * A fill frame is a UI command frame for SAPI 0, P=0
251 * and with an information field of 0 octet length.
253 static const uint8_t lapdm_fill_frame
[] = {
254 0x01, 0x03, 0x01, 0x2b,
255 /* Pending part is to be randomized */
258 /* Make sure that there is no existing primitive */
259 OSMO_ASSERT(lchan
->prim
== NULL
);
262 * Determine what actually should be generated:
263 * TCH in GSM48_CMODE_SIGN: LAPDm fill frame;
264 * TCH in other modes: silence frame;
265 * other channels: LAPDm fill frame.
267 if (CHAN_IS_TCH(chan
) && TCH_MODE_IS_SPEECH(tch_mode
)) {
269 * Silence frame indication
270 * HACK: use actual rsl_cmode!
272 prim_len
= sched_bad_frame_ind(prim_buffer
,
273 RSL_CMOD_SPD_SPEECH
, tch_mode
);
274 } else if (CHAN_IS_TCH(chan
) && TCH_MODE_IS_DATA(tch_mode
)) {
275 /* FIXME: should we do anything for CSD? */
278 uint8_t *cur
= prim_buffer
;
280 if (CHAN_IS_SACCH(chan
)) {
281 /* Add 2-byte SACCH header */
282 /* FIXME: How to get TA and MS Tx Power from l1l->trx->tx_power + l1l->trx->ta? */
283 cur
[0] = cur
[1] = 0x00;
287 /* Copy a fill frame payload */
288 memcpy(cur
, lapdm_fill_frame
, sizeof(lapdm_fill_frame
));
289 cur
+= sizeof(lapdm_fill_frame
);
292 * TS 144.006, section 5.2 "Frame delimitation and fill bits"
293 * Except for the first octet containing fill bits which shall
294 * be set to the binary value "00101011", each fill bit should
295 * be set to a random value when sent by the network.
297 for (i
= cur
- prim_buffer
; i
< GSM_MACBLOCK_LEN
; i
++)
298 prim_buffer
[i
] = (uint8_t) rand();
300 /* Define a prim length */
301 prim_len
= GSM_MACBLOCK_LEN
;
304 /* Nothing to allocate / assign */
308 /* Allocate a new primitive */
309 prim
= talloc_zero_size(lchan
, sizeof(struct trx_ts_prim
) + prim_len
);
313 /* Init primitive header */
314 prim
->payload_len
= prim_len
;
315 prim
->chan
= lchan
->type
;
317 /* Fill in the payload */
318 memcpy(prim
->payload
, prim_buffer
, prim_len
);
320 /* Assign the current prim */
323 LOGP(DSCHD
, LOGL_DEBUG
, "Transmitting a dummy / silence frame "
324 "on lchan=%s\n", trx_lchan_desc
[chan
].name
);
330 * Flushes a queue of primitives
332 * @param list list of prims going to be flushed
334 void sched_prim_flush_queue(struct llist_head
*list
)
336 struct trx_ts_prim
*prim
, *prim_next
;
338 llist_for_each_entry_safe(prim
, prim_next
, list
, list
) {
339 llist_del(&prim
->list
);