blob: 5161e92857924d26a0ea793ae5e2de3f10707657 [file] [log] [blame]
Holger Hans Peter Freyther17c31ce2013-08-24 18:31:27 +02001/* Copied from gprs_bssgp_pcu.cpp
2 *
3 * Copyright (C) 2012 Ivan Klyuchnikov
4 * Copyright (C) 2013 by Holger Hans Peter Freyther
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 this 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 <tbf.h>
22#include <gprs_rlcmac.h>
23#include <gprs_debug.h>
24
25extern "C" {
26#include <osmocom/core/msgb.h>
27}
28
29#include <errno.h>
30#include <string.h>
31
Holger Hans Peter Freytherd1d114f2013-08-24 20:46:18 +020032static inline void tbf_update_ms_class(struct gprs_rlcmac_tbf *tbf,
33 const uint8_t ms_class)
34{
35 if (!tbf->ms_class && ms_class)
36 tbf->ms_class = ms_class;
37}
38
Holger Hans Peter Freyther31d0df92013-08-24 20:42:45 +020039static struct gprs_rlcmac_tbf *tbf_lookup_dl(const uint32_t tlli, const char *imsi)
40{
41 /* TODO: look up by IMSI first, then tlli, then old_tlli */
42 return tbf_by_tlli(tlli, GPRS_RLCMAC_DL_TBF);
43}
44
45static int tbf_append_data(struct gprs_rlcmac_tbf *tbf,
46 struct gprs_rlcmac_bts *bts,
47 const uint8_t ms_class,
48 const uint16_t pdu_delay_csec,
49 const uint8_t *data, const uint16_t len)
50{
51 LOGP(DRLCMAC, LOGL_INFO, "TBF: APPEND TFI: %u TLLI: 0x%08x\n", tbf->tfi, tbf->tlli);
52 if (tbf->state == GPRS_RLCMAC_WAIT_RELEASE) {
53 LOGP(DRLCMAC, LOGL_DEBUG, "TBF in WAIT RELEASE state "
54 "(T3193), so reuse TBF\n");
55 memcpy(tbf->llc_frame, data, len);
56 tbf->llc_length = len;
57 /* reset rlc states */
58 memset(&tbf->dir.dl, 0, sizeof(tbf->dir.dl));
59 /* keep to flags */
60 tbf->state_flags &= GPRS_RLCMAC_FLAG_TO_MASK;
61 tbf->state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH);
Holger Hans Peter Freytherd1d114f2013-08-24 20:46:18 +020062 tbf_update_ms_class(tbf, ms_class);
Holger Hans Peter Freyther31d0df92013-08-24 20:42:45 +020063 tbf_update(tbf);
64 gprs_rlcmac_trigger_downlink_assignment(tbf, tbf, NULL);
65 } else {
66 /* the TBF exists, so we must write it in the queue
67 * we prepend lifetime in front of PDU */
68 struct timeval *tv;
69 struct msgb *llc_msg = msgb_alloc(len + sizeof(*tv),
70 "llc_pdu_queue");
71 if (!llc_msg)
72 return -ENOMEM;
73 tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv));
74
75 uint16_t delay_csec;
76 if (bts->force_llc_lifetime)
77 delay_csec = bts->force_llc_lifetime;
78 else
79 delay_csec = pdu_delay_csec;
80 /* keep timestap at 0 for infinite delay */
81 if (delay_csec != 0xffff) {
82 /* calculate timestamp of timeout */
83 gettimeofday(tv, NULL);
84 tv->tv_usec += (delay_csec % 100) * 10000;
85 tv->tv_sec += delay_csec / 100;
86 if (tv->tv_usec > 999999) {
87 tv->tv_usec -= 1000000;
88 tv->tv_sec++;
89 }
90 }
91 memcpy(msgb_put(llc_msg, len), data, len);
92 msgb_enqueue(&tbf->llc_queue, llc_msg);
Holger Hans Peter Freytherd1d114f2013-08-24 20:46:18 +020093 tbf_update_ms_class(tbf, ms_class);
Holger Hans Peter Freyther31d0df92013-08-24 20:42:45 +020094 }
95
96 return 0;
97}
98
Holger Hans Peter Freyther17c31ce2013-08-24 18:31:27 +020099/**
100 * TODO: split into unit test-able parts...
101 */
102int tbf_handle(struct gprs_rlcmac_bts *bts,
103 const uint32_t tlli, const char *imsi,
Holger Hans Peter Freyther31d0df92013-08-24 20:42:45 +0200104 const uint8_t ms_class, const uint16_t delay_csec,
Holger Hans Peter Freyther17c31ce2013-08-24 18:31:27 +0200105 const uint8_t *data, const uint16_t len)
106{
107 struct gprs_rlcmac_tbf *tbf;
108 int8_t tfi; /* must be signed */
109
110 /* check for existing TBF */
Holger Hans Peter Freyther31d0df92013-08-24 20:42:45 +0200111 tbf = tbf_lookup_dl(tlli, imsi);
112 if (tbf) {
113 int rc = tbf_append_data(tbf, bts, ms_class,
114 delay_csec, data, len);
115 if (rc < 0)
116 return rc;
Holger Hans Peter Freyther17c31ce2013-08-24 18:31:27 +0200117 } else {
118 uint8_t trx, ta, ss;
119 int8_t use_trx;
120 struct gprs_rlcmac_tbf *old_tbf;
121 int rc;
122
123 /* check for uplink data, so we copy our informations */
124 tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_UL_TBF);
125 if (tbf && tbf->dir.ul.contention_resolution_done
126 && !tbf->dir.ul.final_ack_sent) {
127 use_trx = tbf->trx;
128 ta = tbf->ta;
129 ss = 0;
130 old_tbf = tbf;
131 } else {
132 use_trx = -1;
133 /* we already have an uplink TBF, so we use that TA */
134 if (tbf)
135 ta = tbf->ta;
136 else {
137 /* recall TA */
138 rc = recall_timing_advance(tlli);
139 if (rc < 0) {
140 LOGP(DRLCMAC, LOGL_NOTICE, "TA unknown"
141 ", assuming 0\n");
142 ta = 0;
143 } else
144 ta = rc;
145 }
146 ss = 1; /* PCH assignment only allows one timeslot */
147 old_tbf = NULL;
148 }
149
150 // Create new TBF (any TRX)
151 tfi = tfi_find_free(bts, GPRS_RLCMAC_DL_TBF, &trx, use_trx);
152 if (tfi < 0) {
153 LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
154 /* FIXME: send reject */
155 return -EBUSY;
156 }
157 /* set number of downlink slots according to multislot class */
158 tbf = tbf_alloc(bts, tbf, GPRS_RLCMAC_DL_TBF, tfi, trx, ms_class,
159 ss);
160 if (!tbf) {
161 LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
162 /* FIXME: send reject */
163 return -EBUSY;
164 }
165 tbf->tlli = tlli;
166 tbf->tlli_valid = 1;
167 tbf->ta = ta;
168
169 LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [DOWNLINK] START TFI: %d TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);
170
171 /* new TBF, so put first frame */
172 memcpy(tbf->llc_frame, data, len);
173 tbf->llc_length = len;
174
175 /* trigger downlink assignment and set state to ASSIGN.
176 * we don't use old_downlink, so the possible uplink is used
177 * to trigger downlink assignment. if there is no uplink,
178 * AGCH is used. */
179 gprs_rlcmac_trigger_downlink_assignment(tbf, old_tbf, imsi);
180 }
181
182 /* store IMSI for debugging purpose */
183 strncpy(tbf->meas.imsi, imsi, sizeof(tbf->meas.imsi) - 1);
184 return 0;
185}