aboutsummaryrefslogtreecommitdiffstats
path: root/src/tbf.cpp
blob: 816fdb8681796ab7129d173e4d3c526289a94815 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/* Copied from gprs_bssgp_pcu.cpp
 *
 * Copyright (C) 2012 Ivan Klyuchnikov
 * Copyright (C) 2013 by Holger Hans Peter Freyther
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include <tbf.h>
#include <gprs_rlcmac.h>
#include <gprs_debug.h>

extern "C" {
#include <osmocom/core/msgb.h>
}

#include <errno.h>
#include <string.h>

/**
 * TODO: split into unit test-able parts...
 */
int tbf_handle(struct gprs_rlcmac_bts *bts,
		const uint32_t tlli, const char *imsi,
		const uint8_t ms_class, const uint16_t pdu_delay_csec,
		const uint8_t *data, const uint16_t len)
{
	struct gprs_rlcmac_tbf *tbf;
	int8_t tfi; /* must be signed */

	/* check for existing TBF */
	if ((tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_DL_TBF))) {
		LOGP(DRLCMAC, LOGL_INFO, "TBF: APPEND TFI: %u TLLI: 0x%08x\n", tbf->tfi, tbf->tlli);
		if (tbf->state == GPRS_RLCMAC_WAIT_RELEASE) {
			LOGP(DRLCMAC, LOGL_DEBUG, "TBF in WAIT RELEASE state "
				"(T3193), so reuse TBF\n");
			memcpy(tbf->llc_frame, data, len);
			tbf->llc_length = len;
			memset(&tbf->dir.dl, 0, sizeof(tbf->dir.dl)); /* reset
								rlc states */
			tbf->state_flags &= GPRS_RLCMAC_FLAG_TO_MASK; /* keep
				to flags */
			tbf->state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH);
			if (!tbf->ms_class && ms_class)
				tbf->ms_class = ms_class;
			tbf_update(tbf);
			gprs_rlcmac_trigger_downlink_assignment(tbf, tbf, NULL);
		} else {
			/* the TBF exists, so we must write it in the queue
			 * we prepend lifetime in front of PDU */
			struct timeval *tv;
			struct msgb *llc_msg = msgb_alloc(len + sizeof(*tv),
				"llc_pdu_queue");
			if (!llc_msg)
				return -ENOMEM;
			tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv));

			uint16_t delay_csec;
			if (bts->force_llc_lifetime)
				delay_csec = bts->force_llc_lifetime;
			else
				delay_csec = pdu_delay_csec;
			/* keep timestap at 0 for infinite delay */
			if (delay_csec != 0xffff) {
				/* calculate timestamp of timeout */
				gettimeofday(tv, NULL);
				tv->tv_usec += (delay_csec % 100) * 10000;
				tv->tv_sec += delay_csec / 100;
				if (tv->tv_usec > 999999) {
					tv->tv_usec -= 1000000;
					tv->tv_sec++;
				}
			}
			memcpy(msgb_put(llc_msg, len), data, len);
			msgb_enqueue(&tbf->llc_queue, llc_msg);
			/* set ms class for updating TBF */
			if (!tbf->ms_class && ms_class)
				tbf->ms_class = ms_class;
		}
	} else {
		uint8_t trx, ta, ss;
		int8_t use_trx;
		struct gprs_rlcmac_tbf *old_tbf;
		int rc;

		/* check for uplink data, so we copy our informations */
		tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_UL_TBF);
		if (tbf && tbf->dir.ul.contention_resolution_done
		 && !tbf->dir.ul.final_ack_sent) {
			use_trx = tbf->trx;
			ta = tbf->ta;
			ss = 0;
			old_tbf = tbf;
		} else {
			use_trx = -1;
			/* we already have an uplink TBF, so we use that TA */
			if (tbf)
				ta = tbf->ta;
			else {
				/* recall TA */
				rc = recall_timing_advance(tlli);
				if (rc < 0) {
					LOGP(DRLCMAC, LOGL_NOTICE, "TA unknown"
						", assuming 0\n");
					ta = 0;
				} else
					ta = rc;
			}
			ss = 1; /* PCH assignment only allows one timeslot */
			old_tbf = NULL;
		}

		// Create new TBF (any TRX)
		tfi = tfi_find_free(bts, GPRS_RLCMAC_DL_TBF, &trx, use_trx);
		if (tfi < 0) {
			LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
			/* FIXME: send reject */
			return -EBUSY;
		}
		/* set number of downlink slots according to multislot class */
		tbf = tbf_alloc(bts, tbf, GPRS_RLCMAC_DL_TBF, tfi, trx, ms_class,
			ss);
		if (!tbf) {
			LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
			/* FIXME: send reject */
			return -EBUSY;
		}
		tbf->tlli = tlli;
		tbf->tlli_valid = 1;
		tbf->ta = ta;

		LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [DOWNLINK] START TFI: %d TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);

		/* new TBF, so put first frame */
		memcpy(tbf->llc_frame, data, len);
		tbf->llc_length = len;

		/* trigger downlink assignment and set state to ASSIGN.
		 * we don't use old_downlink, so the possible uplink is used
		 * to trigger downlink assignment. if there is no uplink,
		 * AGCH is used. */
		gprs_rlcmac_trigger_downlink_assignment(tbf, old_tbf, imsi);
	}

	/* store IMSI for debugging purpose */
	strncpy(tbf->meas.imsi, imsi, sizeof(tbf->meas.imsi) - 1);
	return 0;
}