blob: da48b6b60285e841cb92be5394b16065176c5887 [file] [log] [blame]
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +04001/* gprs_rlcmac.cpp
2 *
3 * Copyright (C) 2012 Ivan Klyuchnikov
Andreas Eversberg5dac2f02012-06-27 15:52:04 +02004 * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
Holger Hans Peter Freytherd6bd91e2013-08-24 21:16:55 +02005 * Copyright (C) 2013 by Holger Hans Peter Freyther
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +04006 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
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.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22#include <gprs_bssgp_pcu.h>
23#include <pcu_l1_if.h>
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +040024#include <gprs_rlcmac.h>
Holger Hans Peter Freyther67ed34e2013-10-17 17:01:54 +020025#include <bts.h>
Holger Hans Peter Freyther63f29d62013-10-19 19:04:03 +020026#include <encoding.h>
Holger Hans Peter Freyther099535a2013-10-16 17:42:31 +020027#include <tbf.h>
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +040028
Andreas Eversberg53f47252012-07-15 07:10:10 +020029
Andreas Eversberg3b1332c2012-10-03 14:20:53 +020030struct gprs_rlcmac_cs gprs_rlcmac_cs[] = {
31/* frame length data block max payload */
32 { 0, 0, 0 },
33 { 23, 23, 20 }, /* CS-1 */
34 { 34, 33, 30 }, /* CS-2 */
35 { 40, 39, 36 }, /* CS-3 */
36 { 54, 53, 50 }, /* CS-4 */
37};
38
Andreas Eversbergb0c7ea72012-07-13 14:46:03 +020039LLIST_HEAD(gprs_rlcmac_ul_tbfs);
40LLIST_HEAD(gprs_rlcmac_dl_tbfs);
Andreas Eversberg6681bb82012-07-25 08:48:44 +020041extern void *tall_pcu_ctx;
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +040042
Andreas Eversbergb83e2a72012-10-07 15:26:00 +020043#ifdef DEBUG_DIAGRAM
44struct timeval diagram_time = {0,0};
45struct timeval diagram_last_tv = {0,0};
46
47void debug_diagram(int diag, const char *format, ...)
48{
49 va_list ap;
50 char debug[128];
51 char line[1024];
52 struct gprs_rlcmac_tbf *tbf, *tbf_a[16];
53 int max_diag = -1, i;
54 uint64_t diff = 0;
55
56 va_start(ap, format);
57 vsnprintf(debug, sizeof(debug) - 1, format, ap);
58 debug[19] = ' ';
59 debug[20] = '\0';
60 va_end(ap);
61
62 memset(tbf_a, 0, sizeof(tbf_a));
63 llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) {
64 if (tbf->diag < 16) {
65 if (tbf->diag > max_diag)
66 max_diag = tbf->diag;
67 tbf_a[tbf->diag] = tbf;
68 }
69 }
70 llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) {
71 if (tbf->diag < 16) {
72 if (tbf->diag > max_diag)
73 max_diag = tbf->diag;
74 tbf_a[tbf->diag] = tbf;
75 }
76 }
77
78 if (diagram_last_tv.tv_sec) {
79 diff = (uint64_t)(diagram_time.tv_sec -
80 diagram_last_tv.tv_sec) * 1000;
81 diff += diagram_time.tv_usec / 1000;
82 diff -= diagram_last_tv.tv_usec / 1000;
83 }
84 memcpy(&diagram_last_tv, &diagram_time, sizeof(struct timeval));
85
86 if (diff > 0) {
87 if (diff > 99999)
88 strcpy(line, " ... : ");
89 else
90 sprintf(line, "%3d.%03d: ", (int)(diff / 1000),
91 (int)(diff % 1000));
92 for (i = 0; i <= max_diag; i++) {
93 if (tbf_a[i] == NULL) {
94 strcat(line, " ");
95 continue;
96 }
97 if (tbf_a[i]->diag_new) {
98 strcat(line, " | ");
99 continue;
100 }
101 strcat(line, " ");
102 }
103 puts(line);
104 }
105 strcpy(line, " : ");
106 for (i = 0; i <= max_diag; i++) {
107 if (tbf_a[i] == NULL) {
108 strcat(line, " ");
109 continue;
110 }
111 if (tbf_a[i]->diag != diag) {
112 strcat(line, " | ");
113 continue;
114 }
115 if (strlen(debug) < 19) {
116 strcat(line, " ");
117 memcpy(line + strlen(line) - 11 - strlen(debug) / 2,
118 debug, strlen(debug));
119 } else
120 strcat(line, debug);
121 tbf_a[i]->diag_new = 1;
122 }
123 puts(line);
124}
125#endif
126
Holger Hans Peter Freyther483f77a2013-10-16 16:33:00 +0200127/* FIXME: spread resources over multiple TRX. Also add option to use same
Andreas Eversbergb0c7ea72012-07-13 14:46:03 +0200128 * TRX in case of existing TBF for TLLI in the other direction. */
Andreas Eversberg02d7cd22013-01-15 08:59:34 +0100129/* search for free TFI and return TFI, TRX */
Holger Hans Peter Freytherd6bd91e2013-08-24 21:16:55 +0200130int tfi_find_free(struct gprs_rlcmac_bts *bts, enum gprs_rlcmac_tbf_direction dir,
Holger Hans Peter Freytherb67a8a32013-07-28 18:55:14 +0200131 uint8_t *_trx, int8_t use_trx)
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400132{
Andreas Eversberg5dac2f02012-06-27 15:52:04 +0200133 struct gprs_rlcmac_pdch *pdch;
Andreas Eversbergb0c7ea72012-07-13 14:46:03 +0200134 struct gprs_rlcmac_tbf **tbfp;
135 uint8_t trx_from, trx_to, trx, ts, tfi;
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400136
Andreas Eversbergb0c7ea72012-07-13 14:46:03 +0200137 if (use_trx >= 0 && use_trx < 8)
138 trx_from = trx_to = use_trx;
139 else {
140 trx_from = 0;
141 trx_to = 7;
142 }
Andreas Eversbergb0c7ea72012-07-13 14:46:03 +0200143
144 /* on TRX find first enabled TS */
145 for (trx = trx_from; trx <= trx_to; trx++) {
Andreas Eversberg02d7cd22013-01-15 08:59:34 +0100146 for (ts = 0; ts < 8; ts++) {
Andreas Eversberg5dac2f02012-06-27 15:52:04 +0200147 pdch = &bts->trx[trx].pdch[ts];
Holger Hans Peter Freyther17b0d832013-10-19 17:37:48 +0200148 if (!pdch->is_enabled())
Andreas Eversberg5dac2f02012-06-27 15:52:04 +0200149 continue;
150 break;
151 }
152 if (ts < 8)
153 break;
154 }
Andreas Eversbergb0c7ea72012-07-13 14:46:03 +0200155 if (trx > trx_to) {
Andreas Eversberg5dac2f02012-06-27 15:52:04 +0200156 LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH available.\n");
157 return -EINVAL;
158 }
159
160
161 LOGP(DRLCMAC, LOGL_DEBUG, "Searching for first unallocated TFI: "
Andreas Eversbergb0c7ea72012-07-13 14:46:03 +0200162 "TRX=%d first TS=%d\n", trx, ts);
163 if (dir == GPRS_RLCMAC_UL_TBF)
Andreas Eversbergadb2f182012-08-07 17:06:08 +0200164 tbfp = bts->trx[trx].ul_tbf;
Andreas Eversbergb0c7ea72012-07-13 14:46:03 +0200165 else
Andreas Eversbergadb2f182012-08-07 17:06:08 +0200166 tbfp = bts->trx[trx].dl_tbf;
Andreas Eversberg5dac2f02012-06-27 15:52:04 +0200167 for (tfi = 0; tfi < 32; tfi++) {
Andreas Eversbergb0c7ea72012-07-13 14:46:03 +0200168 if (!tbfp[tfi])
Andreas Eversberg5dac2f02012-06-27 15:52:04 +0200169 break;
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400170 }
171
Andreas Eversberg5dac2f02012-06-27 15:52:04 +0200172 if (tfi < 32) {
173 LOGP(DRLCMAC, LOGL_DEBUG, " Found TFI=%d.\n", tfi);
174 *_trx = trx;
Andreas Eversberg5dac2f02012-06-27 15:52:04 +0200175 return tfi;
176 }
177 LOGP(DRLCMAC, LOGL_NOTICE, "No TFI available.\n");
178
179 return -1;
180}
181
Andreas Eversberg07e97cf2012-08-07 16:00:56 +0200182/* starting time for assigning single slot
183 * This offset must be a multiple of 13. */
184#define AGCH_START_OFFSET 52
185
186LLIST_HEAD(gprs_rlcmac_sbas);
187
Holger Hans Peter Freyther173a2402013-10-16 21:47:45 +0200188int sba_alloc(struct gprs_rlcmac_bts *bts,
189 uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta)
Andreas Eversberg07e97cf2012-08-07 16:00:56 +0200190{
191
Andreas Eversberg07e97cf2012-08-07 16:00:56 +0200192 struct gprs_rlcmac_pdch *pdch;
193 struct gprs_rlcmac_sba *sba;
194 uint8_t trx, ts;
195 uint32_t fn;
196
197 sba = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_sba);
198 if (!sba)
199 return -ENOMEM;
200
201 for (trx = 0; trx < 8; trx++) {
202 for (ts = 0; ts < 8; ts++) {
203 pdch = &bts->trx[trx].pdch[ts];
Holger Hans Peter Freyther17b0d832013-10-19 17:37:48 +0200204 if (!pdch->is_enabled())
Andreas Eversberg07e97cf2012-08-07 16:00:56 +0200205 continue;
206 break;
207 }
208 if (ts < 8)
209 break;
210 }
211 if (trx == 8) {
212 LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH available.\n");
Holger Hans Peter Freytherdcc9c392013-10-16 21:51:37 +0200213 talloc_free(sba);
Andreas Eversberg07e97cf2012-08-07 16:00:56 +0200214 return -EINVAL;
215 }
216
217 fn = (pdch->last_rts_fn + AGCH_START_OFFSET) % 2715648;
218
219 sba->trx = trx;
220 sba->ts = ts;
221 sba->fn = fn;
222 sba->ta = ta;
223
224 llist_add(&sba->list, &gprs_rlcmac_sbas);
225
226 *_trx = trx;
227 *_ts = ts;
228 *_fn = fn;
229 return 0;
230}
231
232struct gprs_rlcmac_sba *sba_find(uint8_t trx, uint8_t ts, uint32_t fn)
233{
234 struct gprs_rlcmac_sba *sba;
235
236 llist_for_each_entry(sba, &gprs_rlcmac_sbas, list) {
237 if (sba->trx == trx && sba->ts == ts && sba->fn == fn)
238 return sba;
239 }
240
241 return NULL;
242}
243
Andreas Eversberge6228b32012-07-03 13:36:03 +0200244/* received RLC/MAC block from L1 */
Holger Hans Peter Freyther698b6122013-10-17 13:44:18 +0200245int gprs_rlcmac_rcv_block(struct gprs_rlcmac_bts *bts,
246 uint8_t trx, uint8_t ts, uint8_t *data, uint8_t len,
Andreas Eversberg570b44b2013-03-16 16:15:01 +0100247 uint32_t fn, int8_t rssi)
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400248{
Andreas Eversberge6228b32012-07-03 13:36:03 +0200249 unsigned payload = data[0] >> 6;
250 bitvec *block;
251 int rc = 0;
Ivan Kluchnikov34460b82012-06-27 18:41:04 +0400252
Andreas Eversberge6228b32012-07-03 13:36:03 +0200253 switch (payload) {
254 case GPRS_RLCMAC_DATA_BLOCK:
Holger Hans Peter Freyther698b6122013-10-17 13:44:18 +0200255 rc = gprs_rlcmac_rcv_data_block_acknowledged(bts, trx, ts, data,
Andreas Eversberg570b44b2013-03-16 16:15:01 +0100256 len, rssi);
Andreas Eversberge6228b32012-07-03 13:36:03 +0200257 break;
258 case GPRS_RLCMAC_CONTROL_BLOCK:
259 block = bitvec_alloc(len);
260 if (!block)
261 return -ENOMEM;
262 bitvec_unpack(block, data);
Holger Hans Peter Freyther8d7a6322013-10-17 15:23:49 +0200263 rc = gprs_rlcmac_rcv_control_block(bts, block, trx, ts, fn);
Andreas Eversberge6228b32012-07-03 13:36:03 +0200264 bitvec_free(block);
265 break;
266 case GPRS_RLCMAC_CONTROL_BLOCK_OPT:
267 LOGP(DRLCMAC, LOGL_NOTICE, "GPRS_RLCMAC_CONTROL_BLOCK_OPT block payload is not supported.\n");
Holger Hans Peter Freyther26da8362013-10-16 16:34:09 +0200268 break;
Andreas Eversberge6228b32012-07-03 13:36:03 +0200269 default:
Holger Hans Peter Freyther70862c92013-10-16 16:37:22 +0200270 LOGP(DRLCMAC, LOGL_NOTICE, "Unknown RLCMAC block payload(%u).\n", payload);
Andreas Eversberge6228b32012-07-03 13:36:03 +0200271 rc = -EINVAL;
Ivan Kluchnikov34460b82012-06-27 18:41:04 +0400272 }
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400273
Andreas Eversberge6228b32012-07-03 13:36:03 +0200274 return rc;
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400275}
276
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400277/* Send Uplink unit-data to SGSN. */
Andreas Eversberg3e372d52012-07-06 09:28:15 +0200278int gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf)
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400279{
Andreas Eversberg9a913462012-09-23 06:42:38 +0200280 uint8_t qos_profile[3];
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400281 struct msgb *llc_pdu;
Andreas Eversberg5dac2f02012-06-27 15:52:04 +0200282 unsigned msg_len = NS_HDR_LEN + BSSGP_HDR_LEN + tbf->llc_index;
Holger Hans Peter Freyther90692f92013-07-13 12:51:16 +0200283 struct bssgp_bvc_ctx *bctx = gprs_bssgp_pcu_current_bctx();
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400284
Andreas Eversbergb0c7ea72012-07-13 14:46:03 +0200285 LOGP(DBSSGP, LOGL_INFO, "LLC [PCU -> SGSN] TFI: %u TLLI: 0x%08x len=%d\n", tbf->tfi, tbf->tlli, tbf->llc_index);
Andreas Eversberg3e372d52012-07-06 09:28:15 +0200286 if (!bctx) {
287 LOGP(DBSSGP, LOGL_ERROR, "No bctx\n");
288 return -EIO;
289 }
Ivan Kluchnikovc320d862012-03-18 15:04:48 +0400290
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400291 llc_pdu = msgb_alloc_headroom(msg_len, msg_len,"llc_pdu");
Andreas Eversberg9a913462012-09-23 06:42:38 +0200292 uint8_t *buf = msgb_push(llc_pdu, TL16V_GROSS_LEN(sizeof(uint8_t)*tbf->llc_index));
293 tl16v_put(buf, BSSGP_IE_LLC_PDU, sizeof(uint8_t)*tbf->llc_index, tbf->llc_frame);
294 qos_profile[0] = QOS_PROFILE >> 16;
295 qos_profile[1] = QOS_PROFILE >> 8;
296 qos_profile[2] = QOS_PROFILE;
297 bssgp_tx_ul_ud(bctx, tbf->tlli, qos_profile, llc_pdu);
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400298
Andreas Eversberg3e372d52012-07-06 09:28:15 +0200299 return 0;
Ivan Kluchnikovc7e7f682012-06-29 22:53:15 +0400300}
Andreas Eversberg8c3680d2012-10-08 12:30:56 +0200301
302int gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len,
303 const char *imsi)
304{
305 LOGP(DRLCMAC, LOGL_NOTICE, "TX: [PCU -> BTS] Paging Request (CCCH)\n");
306 bitvec *paging_request = bitvec_alloc(23);
307 bitvec_unhex(paging_request, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
Holger Hans Peter Freyther63f29d62013-10-19 19:04:03 +0200308 int plen = Encoding::write_paging_request(paging_request, ptmsi, ptmsi_len);
Andreas Eversberg8c3680d2012-10-08 12:30:56 +0200309 pcu_l1if_tx_pch(paging_request, plen, (char *)imsi);
310 bitvec_free(paging_request);
311
312 return 0;
313}
Andreas Eversberga004e6a2013-05-13 16:45:21 +0200314
315
316/*
317 * timing advance memory
318 */
319
320/* enable to debug timing advance memory */
321//#define DEBUG_TA
322
323static LLIST_HEAD(gprs_rlcmac_ta_list);
324static int gprs_rlcmac_ta_num = 0;
325
326struct gprs_rlcmac_ta {
327 struct llist_head list;
328 uint32_t tlli;
329 uint8_t ta;
330};
331
332/* remember timing advance of a given TLLI */
333int remember_timing_advance(uint32_t tlli, uint8_t ta)
334{
335 struct gprs_rlcmac_ta *ta_entry;
336
337 /* check for existing entry */
338 llist_for_each_entry(ta_entry, &gprs_rlcmac_ta_list, list) {
339 if (ta_entry->tlli == tlli) {
340#ifdef DEBUG_TA
341 fprintf(stderr, "update %08x %d\n", tlli, ta);
342#endif
343 ta_entry->ta = ta;
344 /* relink to end of list */
345 llist_del(&ta_entry->list);
346 llist_add_tail(&ta_entry->list, &gprs_rlcmac_ta_list);
347 return 0;
348 }
349 }
350
351#ifdef DEBUG_TA
352 fprintf(stderr, "remember %08x %d\n", tlli, ta);
353#endif
354 /* if list is full, remove oldest entry */
355 if (gprs_rlcmac_ta_num == 30) {
356 ta_entry = llist_entry(gprs_rlcmac_ta_list.next,
357 struct gprs_rlcmac_ta, list);
358 llist_del(&ta_entry->list);
359 talloc_free(ta_entry);
360 gprs_rlcmac_ta_num--;
361 }
362
363 /* create new TA entry */
364 ta_entry = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ta);
365 if (!ta_entry)
366 return -ENOMEM;
367
368 ta_entry->tlli = tlli;
369 ta_entry->ta = ta;
370 llist_add_tail(&ta_entry->list, &gprs_rlcmac_ta_list);
371 gprs_rlcmac_ta_num++;
372
373 return 0;
374}
375
376int recall_timing_advance(uint32_t tlli)
377{
378 struct gprs_rlcmac_ta *ta_entry;
379 uint8_t ta;
380
381 llist_for_each_entry(ta_entry, &gprs_rlcmac_ta_list, list) {
382 if (ta_entry->tlli == tlli) {
383 ta = ta_entry->ta;
384#ifdef DEBUG_TA
385 fprintf(stderr, "recall %08x %d\n", tlli, ta);
386#endif
387 return ta;
388 }
389 }
390#ifdef DEBUG_TA
391 fprintf(stderr, "no entry for %08x\n", tlli);
392#endif
393
394 return -EINVAL;
395}
396
397int flush_timing_advance(void)
398{
399 struct gprs_rlcmac_ta *ta_entry;
400 int count = 0;
401
402 while (!llist_empty(&gprs_rlcmac_ta_list)) {
403 ta_entry = llist_entry(gprs_rlcmac_ta_list.next,
404 struct gprs_rlcmac_ta, list);
405#ifdef DEBUG_TA
406 fprintf(stderr, "flush entry %08x %d\n", ta_entry->tlli,
407 ta_entry->ta);
408#endif
409 llist_del(&ta_entry->list);
410 talloc_free(ta_entry);
411 count++;
412 }
413 gprs_rlcmac_ta_num = 0;
414
415 return count;
416}
417