blob: 6a6b7c58dd08a4059eeb0d6ceb737f8efa7c0f73 [file] [log] [blame]
Harald Welte59b04682009-06-10 05:40:52 +08001/* 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 Welte0e3e88e2011-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 Welte59b04682009-06-10 05:40:52 +08009 * (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 Welte0e3e88e2011-01-01 15:25:50 +010014 * GNU Affero General Public License for more details.
Harald Welte59b04682009-06-10 05:40:52 +080015 *
Harald Welte0e3e88e2011-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 Welte59b04682009-06-10 05:40:52 +080018 *
19 */
20
21#include <errno.h>
22#include <stdlib.h>
23#include <string.h>
Harald Welte59b04682009-06-10 05:40:52 +080024
25#include <openbsc/gsm_data.h>
Pablo Neira Ayuso42e41df2011-08-17 22:44:07 +020026#include <osmocom/abis/trau_frame.h>
Harald Welte59b04682009-06-10 05:40:52 +080027#include <openbsc/trau_mux.h>
Pablo Neira Ayuso42e41df2011-08-17 22:44:07 +020028#include <osmocom/abis/subchan_demux.h>
29#include <osmocom/abis/e1_input.h>
Harald Welte59b04682009-06-10 05:40:52 +080030#include <openbsc/debug.h>
Pablo Neira Ayusodd5fff42011-03-22 16:47:59 +010031#include <osmocom/core/talloc.h>
Harald Welte59b04682009-06-10 05:40:52 +080032
Harald Welte9eb9b322011-07-24 02:18:13 +020033/* this corresponds to teh bit-lengths of the individual codec
34 * parameters as indicated in Table 1.1 of TS 06.10 */
35static const uint8_t gsm_fr_map[] = {
Harald Welte3971ad52009-12-19 22:23:05 +010036 6, 6, 5, 5, 4, 4, 3, 3,
37 7, 2, 2, 6, 3, 3, 3, 3,
38 3, 3, 3, 3, 3, 3, 3, 3,
39 3, 7, 2, 2, 6, 3, 3, 3,
40 3, 3, 3, 3, 3, 3, 3, 3,
41 3, 3, 7, 2, 2, 6, 3, 3,
42 3, 3, 3, 3, 3, 3, 3, 3,
43 3, 3, 3, 7, 2, 2, 6, 3,
44 3, 3, 3, 3, 3, 3, 3, 3,
45 3, 3, 3, 3
46};
47
Harald Welte59b04682009-06-10 05:40:52 +080048struct map_entry {
49 struct llist_head list;
50 struct gsm_e1_subslot src, dst;
51};
52
53struct upqueue_entry {
54 struct llist_head list;
55 struct gsm_network *net;
56 struct gsm_e1_subslot src;
Holger Hans Peter Freyther7eb8a9a2011-04-18 17:04:00 +020057 uint32_t callref;
Harald Welte59b04682009-06-10 05:40:52 +080058};
59
60static LLIST_HEAD(ss_map);
61static LLIST_HEAD(ss_upqueue);
62
Harald Welte (local)8751ee92009-08-15 02:30:58 +020063void *tall_map_ctx, *tall_upq_ctx;
Harald Weltea8379772009-06-20 22:36:41 +020064
Harald Welte59b04682009-06-10 05:40:52 +080065/* map one particular subslot to another subslot */
66int trau_mux_map(const struct gsm_e1_subslot *src,
67 const struct gsm_e1_subslot *dst)
68{
Harald Weltea8379772009-06-20 22:36:41 +020069 struct map_entry *me;
70
Harald Weltea8379772009-06-20 22:36:41 +020071 me = talloc(tall_map_ctx, struct map_entry);
Harald Weltecf2ec4a2009-12-17 23:10:46 +010072 if (!me) {
Pablo Neira Ayuso42e41df2011-08-17 22:44:07 +020073 LOGP(DLMIB, LOGL_FATAL, "Out of memory\n");
Harald Welte59b04682009-06-10 05:40:52 +080074 return -ENOMEM;
Harald Weltecf2ec4a2009-12-17 23:10:46 +010075 }
Harald Welte59b04682009-06-10 05:40:52 +080076
77 DEBUGP(DCC, "Setting up TRAU mux map between (e1=%u,ts=%u,ss=%u) "
78 "and (e1=%u,ts=%u,ss=%u)\n",
79 src->e1_nr, src->e1_ts, src->e1_ts_ss,
80 dst->e1_nr, dst->e1_ts, dst->e1_ts_ss);
81
82 /* make sure to get rid of any stale old mappings */
83 trau_mux_unmap(src, 0);
84 trau_mux_unmap(dst, 0);
85
86 memcpy(&me->src, src, sizeof(me->src));
87 memcpy(&me->dst, dst, sizeof(me->dst));
88 llist_add(&me->list, &ss_map);
89
90 return 0;
91}
92
93int trau_mux_map_lchan(const struct gsm_lchan *src,
94 const struct gsm_lchan *dst)
95{
96 struct gsm_e1_subslot *src_ss, *dst_ss;
97
98 src_ss = &src->ts->e1_link;
99 dst_ss = &dst->ts->e1_link;
100
101 return trau_mux_map(src_ss, dst_ss);
102}
103
104
105/* unmap one particular subslot from another subslot */
Holger Hans Peter Freyther7eb8a9a2011-04-18 17:04:00 +0200106int trau_mux_unmap(const struct gsm_e1_subslot *ss, uint32_t callref)
Harald Welte59b04682009-06-10 05:40:52 +0800107{
108 struct map_entry *me, *me2;
109 struct upqueue_entry *ue, *ue2;
110
111 if (ss)
112 llist_for_each_entry_safe(me, me2, &ss_map, list) {
113 if (!memcmp(&me->src, ss, sizeof(*ss)) ||
114 !memcmp(&me->dst, ss, sizeof(*ss))) {
115 llist_del(&me->list);
116 return 0;
117 }
118 }
119 llist_for_each_entry_safe(ue, ue2, &ss_upqueue, list) {
120 if (ue->callref == callref) {
121 llist_del(&ue->list);
122 return 0;
123 }
124 if (ss && !memcmp(&ue->src, ss, sizeof(*ss))) {
125 llist_del(&ue->list);
126 return 0;
127 }
128 }
129 return -ENOENT;
130}
131
132/* look-up an enty in the TRAU mux map */
133static struct gsm_e1_subslot *
134lookup_trau_mux_map(const struct gsm_e1_subslot *src)
135{
136 struct map_entry *me;
137
138 llist_for_each_entry(me, &ss_map, list) {
139 if (!memcmp(&me->src, src, sizeof(*src)))
140 return &me->dst;
141 if (!memcmp(&me->dst, src, sizeof(*src)))
142 return &me->src;
143 }
144 return NULL;
145}
146
147/* look-up an enty in the TRAU upqueue */
148struct upqueue_entry *
149lookup_trau_upqueue(const struct gsm_e1_subslot *src)
150{
151 struct upqueue_entry *ue;
152
153 llist_for_each_entry(ue, &ss_upqueue, list) {
154 if (!memcmp(&ue->src, src, sizeof(*src)))
155 return ue;
156 }
157 return NULL;
158}
159
Holger Hans Peter Freyther7eb8a9a2011-04-18 17:04:00 +0200160static const uint8_t c_bits_check[] = { 0, 0, 0, 1, 0 };
Harald Welte3971ad52009-12-19 22:23:05 +0100161
Harald Welte59b04682009-06-10 05:40:52 +0800162/* we get called by subchan_demux */
163int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
Holger Hans Peter Freyther7eb8a9a2011-04-18 17:04:00 +0200164 const uint8_t *trau_bits, int num_bits)
Harald Welte59b04682009-06-10 05:40:52 +0800165{
166 struct decoded_trau_frame tf;
Holger Hans Peter Freyther7eb8a9a2011-04-18 17:04:00 +0200167 uint8_t trau_bits_out[TRAU_FRAME_BITS];
Harald Welte59b04682009-06-10 05:40:52 +0800168 struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss);
169 struct subch_mux *mx;
Harald Welte03740842009-06-10 23:11:52 +0800170 struct upqueue_entry *ue;
Harald Welte59b04682009-06-10 05:40:52 +0800171 int rc;
172
173 /* decode TRAU, change it to downlink, re-encode */
174 rc = decode_trau_frame(&tf, trau_bits);
175 if (rc)
176 return rc;
177
Harald Welte03740842009-06-10 23:11:52 +0800178 if (!dst_e1_ss) {
Harald Welte3971ad52009-12-19 22:23:05 +0100179 struct msgb *msg;
180 struct gsm_data_frame *frame;
181 unsigned char *data;
182 int i, j, k, l, o;
Harald Welte03740842009-06-10 23:11:52 +0800183 /* frame shall be sent to upqueue */
184 if (!(ue = lookup_trau_upqueue(src_e1_ss)))
185 return -EINVAL;
186 if (!ue->callref)
187 return -EINVAL;
Andreas Eversbergbd107a42009-12-21 01:18:44 +0100188 if (memcmp(tf.c_bits, c_bits_check, sizeof(c_bits_check)))
Pablo Neira Ayuso42e41df2011-08-17 22:44:07 +0200189 DEBUGPC(DLMUX, "illegal trau (C1-C5) %s\n",
Pablo Neira Ayusob1d5a692011-05-07 12:12:48 +0200190 osmo_hexdump(tf.c_bits, sizeof(c_bits_check)));
Harald Welte3971ad52009-12-19 22:23:05 +0100191 msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33,
192 "GSM-DATA");
Harald Welte03740842009-06-10 23:11:52 +0800193 if (!msg)
194 return -ENOMEM;
Harald Welte3971ad52009-12-19 22:23:05 +0100195
196 frame = (struct gsm_data_frame *)msg->data;
197 memset(frame, 0, sizeof(struct gsm_data_frame));
198 data = frame->data;
199 data[0] = 0xd << 4;
200 /* reassemble d-bits */
201 i = 0; /* counts bits */
202 j = 4; /* counts output bits */
203 k = gsm_fr_map[0]-1; /* current number bit in element */
204 l = 0; /* counts element bits */
205 o = 0; /* offset input bits */
206 while (i < 260) {
207 data[j/8] |= (tf.d_bits[k+o] << (7-(j%8)));
208 if (--k < 0) {
209 o += gsm_fr_map[l];
210 k = gsm_fr_map[++l]-1;
211 }
212 i++;
213 j++;
214 }
215 frame->msg_type = GSM_TCHF_FRAME;
Harald Welte03740842009-06-10 23:11:52 +0800216 frame->callref = ue->callref;
Pablo Neira Ayuso93097352011-08-10 02:56:58 +0200217 msgb_put(msg, sizeof(struct gsm_data_frame) + 33);
Harald Welte4f272cc2011-03-03 23:29:05 +0100218 trau_tx_to_mncc(ue->net, msg);
Harald Welte03740842009-06-10 23:11:52 +0800219
220 return 0;
221 }
Harald Welte59b04682009-06-10 05:40:52 +0800222
223 mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
224 if (!mx)
225 return -EINVAL;
226
227 trau_frame_up2down(&tf);
228 encode_trau_frame(trau_bits_out, &tf);
229
230 /* and send it to the muxer */
231 return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out,
232 TRAU_FRAME_BITS);
233}
234
Pablo Neira Ayuso42e41df2011-08-17 22:44:07 +0200235/* callback when a TRAU frame was received */
236int subch_cb(struct subch_demux *dmx, int ch, uint8_t *data, int len,
237 void *_priv)
238{
239 struct e1inp_ts *e1i_ts = _priv;
240 struct gsm_e1_subslot src_ss;
241
242 src_ss.e1_nr = e1i_ts->line->num;
243 src_ss.e1_ts = e1i_ts->num;
244 src_ss.e1_ts_ss = ch;
245
246 return trau_mux_input(&src_ss, data, len);
247}
248
Harald Welte59b04682009-06-10 05:40:52 +0800249/* add receiver instance for lchan and callref */
Holger Hans Peter Freyther7eb8a9a2011-04-18 17:04:00 +0200250int trau_recv_lchan(struct gsm_lchan *lchan, uint32_t callref)
Harald Welte59b04682009-06-10 05:40:52 +0800251{
252 struct gsm_e1_subslot *src_ss;
Harald Weltea8379772009-06-20 22:36:41 +0200253 struct upqueue_entry *ue;
Harald Welte59b04682009-06-10 05:40:52 +0800254
Harald Weltea8379772009-06-20 22:36:41 +0200255 ue = talloc(tall_upq_ctx, struct upqueue_entry);
Harald Welte59b04682009-06-10 05:40:52 +0800256 if (!ue)
257 return -ENOMEM;
258
259 src_ss = &lchan->ts->e1_link;
260
261 DEBUGP(DCC, "Setting up TRAU receiver (e1=%u,ts=%u,ss=%u) "
262 "and (callref 0x%x)\n",
263 src_ss->e1_nr, src_ss->e1_ts, src_ss->e1_ts_ss,
264 callref);
265
266 /* make sure to get rid of any stale old mappings */
267 trau_mux_unmap(src_ss, callref);
268
269 memcpy(&ue->src, src_ss, sizeof(ue->src));
270 ue->net = lchan->ts->trx->bts->network;
271 ue->callref = callref;
272 llist_add(&ue->list, &ss_upqueue);
273
274 return 0;
275}
276
Harald Welte3971ad52009-12-19 22:23:05 +0100277int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame)
Harald Welte59b04682009-06-10 05:40:52 +0800278{
Holger Hans Peter Freyther7eb8a9a2011-04-18 17:04:00 +0200279 uint8_t trau_bits_out[TRAU_FRAME_BITS];
Harald Welte59b04682009-06-10 05:40:52 +0800280 struct gsm_e1_subslot *dst_e1_ss = &lchan->ts->e1_link;
281 struct subch_mux *mx;
Harald Welte3971ad52009-12-19 22:23:05 +0100282 int i, j, k, l, o;
283 unsigned char *data = frame->data;
284 struct decoded_trau_frame tf;
Harald Welte59b04682009-06-10 05:40:52 +0800285
286 mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
287 if (!mx)
288 return -EINVAL;
289
Harald Welte3971ad52009-12-19 22:23:05 +0100290 switch (frame->msg_type) {
291 case GSM_TCHF_FRAME:
292 /* set c-bits and t-bits */
293 tf.c_bits[0] = 1;
294 tf.c_bits[1] = 1;
295 tf.c_bits[2] = 1;
296 tf.c_bits[3] = 0;
297 tf.c_bits[4] = 0;
298 memset(&tf.c_bits[5], 0, 6);
299 memset(&tf.c_bits[11], 1, 10);
300 memset(&tf.t_bits[0], 1, 4);
301 /* reassemble d-bits */
302 i = 0; /* counts bits */
303 j = 4; /* counts input bits */
304 k = gsm_fr_map[0]-1; /* current number bit in element */
305 l = 0; /* counts element bits */
306 o = 0; /* offset output bits */
307 while (i < 260) {
308 tf.d_bits[k+o] = (data[j/8] >> (7-(j%8))) & 1;
309 if (--k < 0) {
310 o += gsm_fr_map[l];
311 k = gsm_fr_map[++l]-1;
312 }
313 i++;
314 j++;
315 }
316 break;
317 default:
Pablo Neira Ayuso42e41df2011-08-17 22:44:07 +0200318 DEBUGPC(DLMUX, "unsupported message type %d\n",
Harald Welte3971ad52009-12-19 22:23:05 +0100319 frame->msg_type);
320 return -EINVAL;
321 }
322
323 encode_trau_frame(trau_bits_out, &tf);
Harald Welte59b04682009-06-10 05:40:52 +0800324
325 /* and send it to the muxer */
326 return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out,
327 TRAU_FRAME_BITS);
328}