blob: 173fd8f38dec8ab8b9946bbc849d19fe45b082e5 [file] [log] [blame]
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +01001/* pdch_ul_controller.c
2 *
3 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
4 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with it program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21#include <unistd.h>
22#include <talloc.h>
23
24#include "pdch_ul_controller.h"
25#include "bts.h"
26#include "sba.h"
27#include "pdch.h"
Pau Espin Pedrol99360a32021-03-09 17:18:12 +010028#include "pcu_utils.h"
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +010029
30/* TS 44.060 Table 10.4.5.1 states maximum RRBP is N + 26. Give extra space for time diff between Tx and Rx? */
31#define MAX_FN_RESERVED (27 + 50)
32
33const struct value_string pdch_ul_node_names[] = {
34 { PDCH_ULC_NODE_TBF_USF, "USF" },
35 { PDCH_ULC_NODE_TBF_POLL, "POLL" },
36 { PDCH_ULC_NODE_SBA, "SBA" },
37 { 0, NULL }
38};
39
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +010040#define GSM_MAX_FN_THRESH (GSM_MAX_FN >> 1)
41/* 0: equal, -1: fn1 BEFORE fn2, 1: fn1 AFTER fn2 */
42static inline int fn_cmp(uint32_t fn1, uint32_t fn2)
43{
44 if (fn1 == fn2)
45 return 0;
46 /* FN1 goes before FN2: */
47 if ((fn1 < fn2 && (fn2 - fn1) < GSM_MAX_FN_THRESH) ||
48 (fn1 > fn2 && (fn1 - fn2) > GSM_MAX_FN_THRESH))
49 return -1;
50 /* FN1 goes after FN2: */
51 return 1;
52}
53
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +010054struct pdch_ulc *pdch_ulc_alloc(struct gprs_rlcmac_pdch *pdch, void *ctx)
55{
56 struct pdch_ulc* ulc;
57 ulc = talloc_zero(ctx, struct pdch_ulc);
58 if (!ulc)
59 return ulc;
60
61 ulc->pdch = pdch;
62 ulc->pool_ctx = talloc_pool(ulc, sizeof(struct pdch_ulc_node) * MAX_FN_RESERVED);
63 return ulc;
64}
65
66struct pdch_ulc_node *pdch_ulc_get_node(struct pdch_ulc *ulc, uint32_t fn)
67{
68 struct rb_node *node;
69 struct pdch_ulc_node *it;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +010070 int res;
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +010071 for (node = rb_first(&ulc->tree_root); node; node = rb_next(node)) {
72 it = container_of(node, struct pdch_ulc_node, node);
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +010073 res = fn_cmp(it->fn, fn);
74 if (res == 0) /* it->fn == fn */
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +010075 return it;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +010076 if (res > 0) /* it->fn AFTER fn */
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +010077 break;
78 }
79 return NULL;
80}
81struct pdch_ulc_node *pdch_ulc_pop_node(struct pdch_ulc *ulc, uint32_t fn)
82{
83 struct pdch_ulc_node *item = pdch_ulc_get_node(ulc, fn);
84 if (!item)
85 return NULL;
86 rb_erase(&item->node, &ulc->tree_root);
87 return item;
88}
89
90struct gprs_rlcmac_sba *pdch_ulc_get_sba(struct pdch_ulc *ulc, uint32_t fn)
91{
92 struct pdch_ulc_node *item = pdch_ulc_get_node(ulc, fn);
93 if (!item || item->type != PDCH_ULC_NODE_SBA)
94 return NULL;
95 return item->sba.sba;
96}
97
Pau Espin Pedrolfd1fbdb2021-03-11 16:59:50 +010098struct gprs_rlcmac_tbf *pdch_ulc_get_tbf_poll(struct pdch_ulc *ulc, uint32_t fn)
99{
100 struct pdch_ulc_node *item = pdch_ulc_get_node(ulc, fn);
101 if (!item || item->type != PDCH_ULC_NODE_TBF_POLL)
102 return NULL;
103 return item->tbf_poll.poll_tbf;
104}
105
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100106bool pdch_ulc_fn_is_free(struct pdch_ulc *ulc, uint32_t fn)
107{
108 return !pdch_ulc_get_node(ulc, fn);
109}
110
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100111int pdch_ulc_get_next_free_rrbp_fn(struct pdch_ulc *ulc, uint32_t fn, uint32_t *poll_fn, unsigned int *rrbp)
112{
113 /* TODO: support other RRBP offsets, see TS 44.060 able 10.4.5.1 */
114 uint32_t new_poll_fn = next_fn(fn, 13);
115 if (!pdch_ulc_fn_is_free(ulc, new_poll_fn)) {
116 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_ERROR, "Polling is already scheduled "
117 "for single block allocation at FN=%u\n", fn);
118 return -EBUSY;
119 }
120
121 *poll_fn = new_poll_fn;
122 *rrbp = 0;
123
124 return 0;
125}
126
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100127static struct pdch_ulc_node *_alloc_node(struct pdch_ulc *ulc, uint32_t fn)
128{
129 struct pdch_ulc_node *node;
130 node = talloc_zero(ulc->pool_ctx, struct pdch_ulc_node);
131 node->fn = fn;
132 return node;
133}
134
135static int pdch_ulc_add_node(struct pdch_ulc *ulc, struct pdch_ulc_node *item)
136{
137 struct rb_node **n = &(ulc->tree_root.rb_node);
138 struct rb_node *parent = NULL;
139
140 while (*n) {
141 struct pdch_ulc_node *it;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100142 int res;
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100143
144 it = container_of(*n, struct pdch_ulc_node, node);
145
146 parent = *n;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100147 res = fn_cmp(item->fn, it->fn);
148 if (res < 0) { /* item->fn "BEFORE" it->fn */
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100149 n = &((*n)->rb_left);
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100150 } else if (res > 0) { /* item->fn "AFTER" it->fn */
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100151 n = &((*n)->rb_right);
152 } else {
153 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_ERROR,
154 "Trying to reserve already reserved FN %u\n",
155 item->fn);
156 return -EEXIST;
157 }
158 }
159
160 rb_link_node(&item->node, parent, n);
161 rb_insert_color(&item->node, &ulc->tree_root);
162 return 0;
163}
164
165int pdch_ulc_reserve_tbf_usf(struct pdch_ulc *ulc, uint32_t fn, struct gprs_rlcmac_ul_tbf *ul_tbf)
166{
167 return 0; /* TODO: implement */
168}
169
170int pdch_ulc_reserve_tbf_poll(struct pdch_ulc *ulc, uint32_t fn, struct gprs_rlcmac_tbf *tbf)
171{
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100172 struct pdch_ulc_node *item = _alloc_node(ulc, fn);
173 item->type = PDCH_ULC_NODE_TBF_POLL;
174 item->tbf_poll.poll_tbf = tbf;
175 return pdch_ulc_add_node(ulc, item);
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100176}
177
178int pdch_ulc_reserve_sba(struct pdch_ulc *ulc, struct gprs_rlcmac_sba *sba)
179{
180 struct pdch_ulc_node *item = _alloc_node(ulc, sba->fn);
181 item->type = PDCH_ULC_NODE_SBA;
182 item->sba.sba = sba;
183 return pdch_ulc_add_node(ulc, item);
184}
185
186int pdch_ulc_release_fn(struct pdch_ulc *ulc, uint32_t fn)
187{
188 struct pdch_ulc_node *item = pdch_ulc_get_node(ulc, fn);
189 if (!item)
190 return -ENOKEY;
191 rb_erase(&item->node, &ulc->tree_root);
192 talloc_free(item);
193 return 0;
194}
195
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100196void pdch_ulc_release_tbf(struct pdch_ulc *ulc, const struct gprs_rlcmac_tbf *tbf)
197{
198 bool tree_modified;
199 do {
200 struct rb_node *node;
201 struct pdch_ulc_node *item;
202 const struct gprs_rlcmac_tbf *item_tbf;
203
204 tree_modified = false;
205 for (node = rb_first(&ulc->tree_root); node; node = rb_next(node)) {
206 item = container_of(node, struct pdch_ulc_node, node);
207 switch (item->type) {
208 case PDCH_ULC_NODE_SBA:
209 continue;
210 case PDCH_ULC_NODE_TBF_POLL:
211 item_tbf = item->tbf_poll.poll_tbf;
212 break;
213 case PDCH_ULC_NODE_TBF_USF:
214 item_tbf = (struct gprs_rlcmac_tbf *)item->tbf_usf.ul_tbf;
215 break;
216 }
217 if (item_tbf != tbf)
218 continue;
219 /* One entry found, remove it from tree and restart
220 * search from start (to avoid traverse continue from
221 * no-more existant node */
222 tree_modified = true;
223 rb_erase(&item->node, &ulc->tree_root);
224 talloc_free(item);
225 break;
226 }
227 } while (tree_modified);
228}
229
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100230void pdch_ulc_expire_fn(struct pdch_ulc *ulc, uint32_t fn)
231{
232 struct gprs_rlcmac_sba *sba;
233 struct pdch_ulc_node *item;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100234 int res;
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100235
236 struct rb_node *first;
237 while((first = rb_first(&ulc->tree_root))) {
238 item = container_of(first, struct pdch_ulc_node, node);
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100239 res = fn_cmp(item->fn, fn);
240 if (res > 0) /* item->fn AFTER fn */
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100241 break;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100242 if (res < 0) { /* item->fn BEFORE fn */
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100243 /* Sanity check: */
244 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_ERROR,
245 "Expiring FN=%" PRIu32 " but previous FN=%" PRIu32 " is still reserved!\n",
246 fn, item->fn);
247 }
248 rb_erase(&item->node, &ulc->tree_root);
249
250 switch (item->type) {
251 case PDCH_ULC_NODE_TBF_USF:
252 /* TODO: increase N3...*/
253 break;
254 case PDCH_ULC_NODE_TBF_POLL:
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100255 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_NOTICE,
256 "Timeout for registered POLL (FN=%u): %s\n",
257 item->fn, tbf_name(item->tbf_poll.poll_tbf));
258 tbf_poll_timeout(item->tbf_poll.poll_tbf);
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100259 break;
260 case PDCH_ULC_NODE_SBA:
261 sba = item->sba.sba;
262 LOGPDCH(sba->pdch, DRLCMAC, LOGL_NOTICE,
263 "Timeout for registered SBA (FN=%u, TA=%u)\n",
264 sba->fn, sba->ta);
265 sba_timeout(sba);
266 break;
267 }
268
269 talloc_free(item);
270 }
271}