blob: 3260ff57544557bf78591676bd3344b940afd6c0 [file] [log] [blame]
Harald Welte1fa60c82009-02-09 18:13:26 +00001/* Simple TRAU frame reflector to route voice calls */
2
3/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
4 * All Rights Reserved
5 *
6 * This program is free software; you can redistribute it and/or modify
Harald Welte9af6ddf2011-01-01 15:25:50 +01007 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
Harald Welte1fa60c82009-02-09 18:13:26 +00009 * (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
Harald Welte9af6ddf2011-01-01 15:25:50 +010014 * GNU Affero General Public License for more details.
Harald Welte1fa60c82009-02-09 18:13:26 +000015 *
Harald Welte9af6ddf2011-01-01 15:25:50 +010016 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
Harald Welte1fa60c82009-02-09 18:13:26 +000018 *
19 */
20
21#include <errno.h>
22#include <stdlib.h>
23#include <string.h>
Harald Welte1fa60c82009-02-09 18:13:26 +000024
25#include <openbsc/gsm_data.h>
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +020026#include <osmocom/abis/trau_frame.h>
Harald Welte1fa60c82009-02-09 18:13:26 +000027#include <openbsc/trau_mux.h>
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +020028#include <osmocom/abis/subchan_demux.h>
29#include <osmocom/abis/e1_input.h>
Harald Welte41e16682009-02-18 03:34:55 +000030#include <openbsc/debug.h>
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010031#include <osmocom/core/talloc.h>
Alexander Huemer0a654612011-09-06 00:09:49 +020032#include <openbsc/trau_upqueue.h>
Harald Welte1fa60c82009-02-09 18:13:26 +000033
Harald Welte07b7bd72011-07-24 02:18:13 +020034/* this corresponds to teh bit-lengths of the individual codec
35 * parameters as indicated in Table 1.1 of TS 06.10 */
36static const uint8_t gsm_fr_map[] = {
Harald Welteda7ab742009-12-19 22:23:05 +010037 6, 6, 5, 5, 4, 4, 3, 3,
38 7, 2, 2, 6, 3, 3, 3, 3,
39 3, 3, 3, 3, 3, 3, 3, 3,
40 3, 7, 2, 2, 6, 3, 3, 3,
41 3, 3, 3, 3, 3, 3, 3, 3,
42 3, 3, 7, 2, 2, 6, 3, 3,
43 3, 3, 3, 3, 3, 3, 3, 3,
44 3, 3, 3, 7, 2, 2, 6, 3,
45 3, 3, 3, 3, 3, 3, 3, 3,
46 3, 3, 3, 3
47};
48
Harald Welte1fa60c82009-02-09 18:13:26 +000049struct map_entry {
50 struct llist_head list;
51 struct gsm_e1_subslot src, dst;
52};
53
Harald Welte45b407a2009-05-23 15:51:12 +000054struct upqueue_entry {
55 struct llist_head list;
56 struct gsm_network *net;
57 struct gsm_e1_subslot src;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020058 uint32_t callref;
Harald Welte45b407a2009-05-23 15:51:12 +000059};
60
Harald Welte1fa60c82009-02-09 18:13:26 +000061static LLIST_HEAD(ss_map);
Harald Welte45b407a2009-05-23 15:51:12 +000062static LLIST_HEAD(ss_upqueue);
Harald Welte1fa60c82009-02-09 18:13:26 +000063
Harald Welte (local)d19e58b2009-08-15 02:30:58 +020064void *tall_map_ctx, *tall_upq_ctx;
Harald Welte2cf161b2009-06-20 22:36:41 +020065
Harald Welte1fa60c82009-02-09 18:13:26 +000066/* map one particular subslot to another subslot */
67int trau_mux_map(const struct gsm_e1_subslot *src,
68 const struct gsm_e1_subslot *dst)
69{
Harald Welte2cf161b2009-06-20 22:36:41 +020070 struct map_entry *me;
71
Harald Welte2cf161b2009-06-20 22:36:41 +020072 me = talloc(tall_map_ctx, struct map_entry);
Harald Welteb1d4c8e2009-12-17 23:10:46 +010073 if (!me) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +020074 LOGP(DLMIB, LOGL_FATAL, "Out of memory\n");
Harald Welte1fa60c82009-02-09 18:13:26 +000075 return -ENOMEM;
Harald Welteb1d4c8e2009-12-17 23:10:46 +010076 }
Harald Welte1fa60c82009-02-09 18:13:26 +000077
Harald Weltedbd2ea82009-02-19 17:07:01 +000078 DEBUGP(DCC, "Setting up TRAU mux map between (e1=%u,ts=%u,ss=%u) "
79 "and (e1=%u,ts=%u,ss=%u)\n",
80 src->e1_nr, src->e1_ts, src->e1_ts_ss,
81 dst->e1_nr, dst->e1_ts, dst->e1_ts_ss);
82
Harald Welte26aa6a12009-02-19 15:14:23 +000083 /* make sure to get rid of any stale old mappings */
Harald Welte45b407a2009-05-23 15:51:12 +000084 trau_mux_unmap(src, 0);
85 trau_mux_unmap(dst, 0);
Harald Welte26aa6a12009-02-19 15:14:23 +000086
Harald Welte1fa60c82009-02-09 18:13:26 +000087 memcpy(&me->src, src, sizeof(me->src));
88 memcpy(&me->dst, dst, sizeof(me->dst));
89 llist_add(&me->list, &ss_map);
90
91 return 0;
92}
93
Harald Welte26aa6a12009-02-19 15:14:23 +000094int trau_mux_map_lchan(const struct gsm_lchan *src,
95 const struct gsm_lchan *dst)
96{
97 struct gsm_e1_subslot *src_ss, *dst_ss;
98
99 src_ss = &src->ts->e1_link;
100 dst_ss = &dst->ts->e1_link;
101
102 return trau_mux_map(src_ss, dst_ss);
103}
104
105
Harald Welte1fa60c82009-02-09 18:13:26 +0000106/* unmap one particular subslot from another subslot */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200107int trau_mux_unmap(const struct gsm_e1_subslot *ss, uint32_t callref)
Harald Welte1fa60c82009-02-09 18:13:26 +0000108{
109 struct map_entry *me, *me2;
Harald Welte45b407a2009-05-23 15:51:12 +0000110 struct upqueue_entry *ue, *ue2;
Harald Welte1fa60c82009-02-09 18:13:26 +0000111
Harald Welte45b407a2009-05-23 15:51:12 +0000112 if (ss)
113 llist_for_each_entry_safe(me, me2, &ss_map, list) {
114 if (!memcmp(&me->src, ss, sizeof(*ss)) ||
115 !memcmp(&me->dst, ss, sizeof(*ss))) {
116 llist_del(&me->list);
117 return 0;
118 }
119 }
120 llist_for_each_entry_safe(ue, ue2, &ss_upqueue, list) {
121 if (ue->callref == callref) {
122 llist_del(&ue->list);
123 return 0;
124 }
125 if (ss && !memcmp(&ue->src, ss, sizeof(*ss))) {
126 llist_del(&ue->list);
Harald Welte1fa60c82009-02-09 18:13:26 +0000127 return 0;
128 }
129 }
130 return -ENOENT;
131}
132
133/* look-up an enty in the TRAU mux map */
134static struct gsm_e1_subslot *
135lookup_trau_mux_map(const struct gsm_e1_subslot *src)
136{
137 struct map_entry *me;
138
139 llist_for_each_entry(me, &ss_map, list) {
140 if (!memcmp(&me->src, src, sizeof(*src)))
141 return &me->dst;
142 if (!memcmp(&me->dst, src, sizeof(*src)))
143 return &me->src;
144 }
145 return NULL;
146}
147
Harald Welte45b407a2009-05-23 15:51:12 +0000148/* look-up an enty in the TRAU upqueue */
149struct upqueue_entry *
150lookup_trau_upqueue(const struct gsm_e1_subslot *src)
151{
152 struct upqueue_entry *ue;
153
154 llist_for_each_entry(ue, &ss_upqueue, list) {
155 if (!memcmp(&ue->src, src, sizeof(*src)))
156 return ue;
157 }
158 return NULL;
159}
160
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200161static const uint8_t c_bits_check[] = { 0, 0, 0, 1, 0 };
Harald Welteda7ab742009-12-19 22:23:05 +0100162
Harald Welte1fa60c82009-02-09 18:13:26 +0000163/* we get called by subchan_demux */
164int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200165 const uint8_t *trau_bits, int num_bits)
Harald Welte1fa60c82009-02-09 18:13:26 +0000166{
167 struct decoded_trau_frame tf;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200168 uint8_t trau_bits_out[TRAU_FRAME_BITS];
Harald Welte1fa60c82009-02-09 18:13:26 +0000169 struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss);
170 struct subch_mux *mx;
Harald Welte4bfdfe72009-06-10 23:11:52 +0800171 struct upqueue_entry *ue;
Harald Welte41e16682009-02-18 03:34:55 +0000172 int rc;
Harald Welte1fa60c82009-02-09 18:13:26 +0000173
Harald Welte45b407a2009-05-23 15:51:12 +0000174 /* decode TRAU, change it to downlink, re-encode */
175 rc = decode_trau_frame(&tf, trau_bits);
176 if (rc)
177 return rc;
178
Harald Welte4bfdfe72009-06-10 23:11:52 +0800179 if (!dst_e1_ss) {
Harald Welteda7ab742009-12-19 22:23:05 +0100180 struct msgb *msg;
181 struct gsm_data_frame *frame;
182 unsigned char *data;
183 int i, j, k, l, o;
Harald Welte4bfdfe72009-06-10 23:11:52 +0800184 /* frame shall be sent to upqueue */
185 if (!(ue = lookup_trau_upqueue(src_e1_ss)))
186 return -EINVAL;
187 if (!ue->callref)
188 return -EINVAL;
Andreas Eversberg56315f02009-12-21 01:18:44 +0100189 if (memcmp(tf.c_bits, c_bits_check, sizeof(c_bits_check)))
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200190 DEBUGPC(DLMUX, "illegal trau (C1-C5) %s\n",
Pablo Neira Ayusoc0d17f22011-05-07 12:12:48 +0200191 osmo_hexdump(tf.c_bits, sizeof(c_bits_check)));
Harald Welteda7ab742009-12-19 22:23:05 +0100192 msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33,
193 "GSM-DATA");
Harald Welte4bfdfe72009-06-10 23:11:52 +0800194 if (!msg)
195 return -ENOMEM;
Harald Welteda7ab742009-12-19 22:23:05 +0100196
197 frame = (struct gsm_data_frame *)msg->data;
198 memset(frame, 0, sizeof(struct gsm_data_frame));
199 data = frame->data;
200 data[0] = 0xd << 4;
201 /* reassemble d-bits */
202 i = 0; /* counts bits */
203 j = 4; /* counts output bits */
204 k = gsm_fr_map[0]-1; /* current number bit in element */
205 l = 0; /* counts element bits */
206 o = 0; /* offset input bits */
207 while (i < 260) {
208 data[j/8] |= (tf.d_bits[k+o] << (7-(j%8)));
209 if (--k < 0) {
210 o += gsm_fr_map[l];
211 k = gsm_fr_map[++l]-1;
212 }
213 i++;
214 j++;
215 }
216 frame->msg_type = GSM_TCHF_FRAME;
Harald Welte4bfdfe72009-06-10 23:11:52 +0800217 frame->callref = ue->callref;
Pablo Neira Ayusof7dc7612011-08-10 02:56:58 +0200218 msgb_put(msg, sizeof(struct gsm_data_frame) + 33);
Harald Welte31c00f72011-03-03 23:29:05 +0100219 trau_tx_to_mncc(ue->net, msg);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800220
221 return 0;
222 }
Harald Welte1fa60c82009-02-09 18:13:26 +0000223
224 mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
225 if (!mx)
226 return -EINVAL;
227
Harald Welte1fa60c82009-02-09 18:13:26 +0000228 trau_frame_up2down(&tf);
229 encode_trau_frame(trau_bits_out, &tf);
230
231 /* and send it to the muxer */
232 return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out,
233 TRAU_FRAME_BITS);
234}
Harald Welte45b407a2009-05-23 15:51:12 +0000235
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200236/* callback when a TRAU frame was received */
237int subch_cb(struct subch_demux *dmx, int ch, uint8_t *data, int len,
238 void *_priv)
239{
240 struct e1inp_ts *e1i_ts = _priv;
241 struct gsm_e1_subslot src_ss;
242
243 src_ss.e1_nr = e1i_ts->line->num;
244 src_ss.e1_ts = e1i_ts->num;
245 src_ss.e1_ts_ss = ch;
246
247 return trau_mux_input(&src_ss, data, len);
248}
249
Harald Welte45b407a2009-05-23 15:51:12 +0000250/* add receiver instance for lchan and callref */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200251int trau_recv_lchan(struct gsm_lchan *lchan, uint32_t callref)
Harald Welte45b407a2009-05-23 15:51:12 +0000252{
253 struct gsm_e1_subslot *src_ss;
Harald Welte2cf161b2009-06-20 22:36:41 +0200254 struct upqueue_entry *ue;
Harald Welte45b407a2009-05-23 15:51:12 +0000255
Harald Welte2cf161b2009-06-20 22:36:41 +0200256 ue = talloc(tall_upq_ctx, struct upqueue_entry);
Harald Welte45b407a2009-05-23 15:51:12 +0000257 if (!ue)
258 return -ENOMEM;
259
260 src_ss = &lchan->ts->e1_link;
261
262 DEBUGP(DCC, "Setting up TRAU receiver (e1=%u,ts=%u,ss=%u) "
263 "and (callref 0x%x)\n",
264 src_ss->e1_nr, src_ss->e1_ts, src_ss->e1_ts_ss,
265 callref);
266
267 /* make sure to get rid of any stale old mappings */
268 trau_mux_unmap(src_ss, callref);
269
270 memcpy(&ue->src, src_ss, sizeof(ue->src));
271 ue->net = lchan->ts->trx->bts->network;
272 ue->callref = callref;
273 llist_add(&ue->list, &ss_upqueue);
274
275 return 0;
276}
277
Harald Welteda7ab742009-12-19 22:23:05 +0100278int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame)
Harald Welte45b407a2009-05-23 15:51:12 +0000279{
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200280 uint8_t trau_bits_out[TRAU_FRAME_BITS];
Harald Welte45b407a2009-05-23 15:51:12 +0000281 struct gsm_e1_subslot *dst_e1_ss = &lchan->ts->e1_link;
282 struct subch_mux *mx;
Harald Welteda7ab742009-12-19 22:23:05 +0100283 int i, j, k, l, o;
284 unsigned char *data = frame->data;
285 struct decoded_trau_frame tf;
Harald Welte45b407a2009-05-23 15:51:12 +0000286
287 mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
288 if (!mx)
289 return -EINVAL;
290
Harald Welteda7ab742009-12-19 22:23:05 +0100291 switch (frame->msg_type) {
292 case GSM_TCHF_FRAME:
293 /* set c-bits and t-bits */
294 tf.c_bits[0] = 1;
295 tf.c_bits[1] = 1;
296 tf.c_bits[2] = 1;
297 tf.c_bits[3] = 0;
298 tf.c_bits[4] = 0;
299 memset(&tf.c_bits[5], 0, 6);
300 memset(&tf.c_bits[11], 1, 10);
301 memset(&tf.t_bits[0], 1, 4);
302 /* reassemble d-bits */
303 i = 0; /* counts bits */
304 j = 4; /* counts input bits */
305 k = gsm_fr_map[0]-1; /* current number bit in element */
306 l = 0; /* counts element bits */
307 o = 0; /* offset output bits */
308 while (i < 260) {
309 tf.d_bits[k+o] = (data[j/8] >> (7-(j%8))) & 1;
310 if (--k < 0) {
311 o += gsm_fr_map[l];
312 k = gsm_fr_map[++l]-1;
313 }
314 i++;
315 j++;
316 }
317 break;
318 default:
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200319 DEBUGPC(DLMUX, "unsupported message type %d\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100320 frame->msg_type);
321 return -EINVAL;
322 }
323
324 encode_trau_frame(trau_bits_out, &tf);
Harald Welte45b407a2009-05-23 15:51:12 +0000325
326 /* and send it to the muxer */
327 return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out,
328 TRAU_FRAME_BITS);
329}