trxcon: Prefix SACCH fill frame with L1 header
[osmocom-bb.git] / src / host / trxcon / sched_prim.c
blob2ee06d737ee07d9481f140ac2006b6c6cb567ad6
1 /*
2 * OsmocomBB <-> SDR connection bridge
3 * TDMA scheduler: primitive management
5 * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
7 * All Rights Reserved
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.
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <talloc.h>
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"
39 #include "trx_if.h"
40 #include "logging.h"
42 /**
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;
59 uint8_t len;
61 /* Determine lchan type */
62 lchan_type = sched_trx_chan_nr2lchan_type(chan_nr, link_id);
63 if (!lchan_type) {
64 LOGP(DSCH, LOGL_ERROR, "Couldn't determine lchan type "
65 "for chan_nr=%02x and link_id=%02x\n", chan_nr, link_id);
66 return -EINVAL;
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");
77 return -ENOMEM;
80 /* Init primitive header */
81 new_prim->payload_len = pl_len;
82 new_prim->chan = lchan_type;
84 /* Set external pointer */
85 *prim = new_prim;
87 return 0;
90 /**
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)
102 struct trx_ts *ts;
103 uint8_t tn;
105 /* Determine TS index */
106 tn = chan_nr & 0x7;
107 if (tn > 7) {
108 LOGP(DSCH, LOGL_ERROR, "Incorrect TS index %u\n", tn);
109 return -EINVAL;
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);
116 return -EINVAL;
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);
128 return 0;
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))
149 facch = i;
151 /* Find one TCH frame */
152 if (!tch && PRIM_IS_TCH(i))
153 tch = i;
155 /* If both are found */
156 if (facch && tch)
157 break;
160 /* Prioritize FACCH */
161 if (facch && tch) {
162 /* We found a pair, dequeue both */
163 llist_del(&facch->list);
164 llist_del(&tch->list);
166 /* Drop TCH */
167 talloc_free(tch);
169 /* FACCH replaces TCH */
170 return facch;
171 } else if (facch) {
172 /* Only FACCH was found */
173 llist_del(&facch->list);
174 return facch;
175 } else if (tch) {
176 /* Only TCH was found */
177 llist_del(&tch->list);
178 return tch;
182 * Nothing was found,
183 * e.g. only SACCH frames are in queue
185 return NULL;
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))
203 return NULL;
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);
212 return prim;
216 return NULL;
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);
228 lchan->prim = NULL;
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];
245 size_t prim_len = 0;
246 int i;
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? */
276 return 0;
277 } else {
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;
284 cur += 2;
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 */
305 if (!prim_len)
306 return 0;
308 /* Allocate a new primitive */
309 prim = talloc_zero_size(lchan, sizeof(struct trx_ts_prim) + prim_len);
310 if (prim == NULL)
311 return -ENOMEM;
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 */
321 lchan->prim = prim;
323 LOGP(DSCHD, LOGL_DEBUG, "Transmitting a dummy / silence frame "
324 "on lchan=%s\n", trx_lchan_desc[chan].name);
326 return 0;
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);
340 talloc_free(prim);