blob: b8b90bd02cccdfef2cbe84a21e33b8778c0f4bde [file] [log] [blame]
Jonathan Santos03fd8d02011-05-25 13:54:02 -04001/* 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
7 * 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
9 * (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 Affero General Public License for more details.
15 *
16 * 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/>.
18 *
19 */
20
21#include <errno.h>
22#include <stdlib.h>
23#include <string.h>
Jonathan Santos03fd8d02011-05-25 13:54:02 -040024
25#include <openbsc/gsm_data.h>
26#include <openbsc/trau_frame.h>
27#include <openbsc/trau_mux.h>
28#include <openbsc/subchan_demux.h>
29#include <openbsc/e1_input.h>
30#include <openbsc/debug.h>
Jonathan Santos5a45b152011-08-17 15:33:57 -040031#include <osmocom/core/talloc.h>
Jonathan Santos03fd8d02011-05-25 13:54:02 -040032
Jonathan Santos5a45b152011-08-17 15:33:57 -040033uint8_t gsm_fr_map[] = {
Jonathan Santos03fd8d02011-05-25 13:54:02 -040034 6, 6, 5, 5, 4, 4, 3, 3,
35 7, 2, 2, 6, 3, 3, 3, 3,
36 3, 3, 3, 3, 3, 3, 3, 3,
37 3, 7, 2, 2, 6, 3, 3, 3,
38 3, 3, 3, 3, 3, 3, 3, 3,
39 3, 3, 7, 2, 2, 6, 3, 3,
40 3, 3, 3, 3, 3, 3, 3, 3,
41 3, 3, 3, 7, 2, 2, 6, 3,
42 3, 3, 3, 3, 3, 3, 3, 3,
43 3, 3, 3, 3
44};
45
46struct map_entry {
47 struct llist_head list;
48 struct gsm_e1_subslot src, dst;
49};
50
51struct upqueue_entry {
52 struct llist_head list;
53 struct gsm_network *net;
54 struct gsm_e1_subslot src;
Jonathan Santos5a45b152011-08-17 15:33:57 -040055 uint32_t callref;
Jonathan Santos03fd8d02011-05-25 13:54:02 -040056};
57
58static LLIST_HEAD(ss_map);
59static LLIST_HEAD(ss_upqueue);
60
61void *tall_map_ctx, *tall_upq_ctx;
62
63/* map one particular subslot to another subslot */
64int trau_mux_map(const struct gsm_e1_subslot *src,
65 const struct gsm_e1_subslot *dst)
66{
67 struct map_entry *me;
68
69 me = talloc(tall_map_ctx, struct map_entry);
70 if (!me) {
71 LOGP(DMIB, LOGL_FATAL, "Out of memory\n");
72 return -ENOMEM;
73 }
74
75 DEBUGP(DCC, "Setting up TRAU mux map between (e1=%u,ts=%u,ss=%u) "
76 "and (e1=%u,ts=%u,ss=%u)\n",
77 src->e1_nr, src->e1_ts, src->e1_ts_ss,
78 dst->e1_nr, dst->e1_ts, dst->e1_ts_ss);
79
80 /* make sure to get rid of any stale old mappings */
81 trau_mux_unmap(src, 0);
82 trau_mux_unmap(dst, 0);
83
84 memcpy(&me->src, src, sizeof(me->src));
85 memcpy(&me->dst, dst, sizeof(me->dst));
86 llist_add(&me->list, &ss_map);
87
88 return 0;
89}
90
91int trau_mux_map_lchan(const struct gsm_lchan *src,
92 const struct gsm_lchan *dst)
93{
94 struct gsm_e1_subslot *src_ss, *dst_ss;
95
96 src_ss = &src->ts->e1_link;
97 dst_ss = &dst->ts->e1_link;
98
99 return trau_mux_map(src_ss, dst_ss);
100}
101
102
103/* unmap one particular subslot from another subslot */
Jonathan Santos5a45b152011-08-17 15:33:57 -0400104int trau_mux_unmap(const struct gsm_e1_subslot *ss, uint32_t callref)
Jonathan Santos03fd8d02011-05-25 13:54:02 -0400105{
106 struct map_entry *me, *me2;
107 struct upqueue_entry *ue, *ue2;
108
109 if (ss)
110 llist_for_each_entry_safe(me, me2, &ss_map, list) {
111 if (!memcmp(&me->src, ss, sizeof(*ss)) ||
112 !memcmp(&me->dst, ss, sizeof(*ss))) {
113 llist_del(&me->list);
114 return 0;
115 }
116 }
117 llist_for_each_entry_safe(ue, ue2, &ss_upqueue, list) {
118 if (ue->callref == callref) {
119 llist_del(&ue->list);
120 return 0;
121 }
122 if (ss && !memcmp(&ue->src, ss, sizeof(*ss))) {
123 llist_del(&ue->list);
124 return 0;
125 }
126 }
127 return -ENOENT;
128}
129
130/* look-up an enty in the TRAU mux map */
131static struct gsm_e1_subslot *
132lookup_trau_mux_map(const struct gsm_e1_subslot *src)
133{
134 struct map_entry *me;
135
136 llist_for_each_entry(me, &ss_map, list) {
137 if (!memcmp(&me->src, src, sizeof(*src)))
138 return &me->dst;
139 if (!memcmp(&me->dst, src, sizeof(*src)))
140 return &me->src;
141 }
142 return NULL;
143}
144
145/* look-up an enty in the TRAU upqueue */
146struct upqueue_entry *
147lookup_trau_upqueue(const struct gsm_e1_subslot *src)
148{
149 struct upqueue_entry *ue;
150
151 llist_for_each_entry(ue, &ss_upqueue, list) {
152 if (!memcmp(&ue->src, src, sizeof(*src)))
153 return ue;
154 }
155 return NULL;
156}
157
Jonathan Santos5a45b152011-08-17 15:33:57 -0400158static const uint8_t c_bits_check[] = { 0, 0, 0, 1, 0 };
Jonathan Santos03fd8d02011-05-25 13:54:02 -0400159
160/* we get called by subchan_demux */
161int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
Jonathan Santos5a45b152011-08-17 15:33:57 -0400162 const uint8_t *trau_bits, int num_bits)
Jonathan Santos03fd8d02011-05-25 13:54:02 -0400163{
164 struct decoded_trau_frame tf;
Jonathan Santos5a45b152011-08-17 15:33:57 -0400165 uint8_t trau_bits_out[TRAU_FRAME_BITS];
Jonathan Santos03fd8d02011-05-25 13:54:02 -0400166 struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss);
167 struct subch_mux *mx;
168 struct upqueue_entry *ue;
169 int rc;
170
171 /* decode TRAU, change it to downlink, re-encode */
172 rc = decode_trau_frame(&tf, trau_bits);
173 if (rc)
174 return rc;
175
176 if (!dst_e1_ss) {
177 struct msgb *msg;
178 struct gsm_data_frame *frame;
179 unsigned char *data;
180 int i, j, k, l, o;
181 /* frame shall be sent to upqueue */
182 if (!(ue = lookup_trau_upqueue(src_e1_ss)))
183 return -EINVAL;
184 if (!ue->callref)
185 return -EINVAL;
186 if (memcmp(tf.c_bits, c_bits_check, sizeof(c_bits_check)))
187 DEBUGPC(DMUX, "illegal trau (C1-C5) %s\n",
Jonathan Santos5a45b152011-08-17 15:33:57 -0400188 osmo_hexdump(tf.c_bits, sizeof(c_bits_check)));
Jonathan Santos03fd8d02011-05-25 13:54:02 -0400189 msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33,
190 "GSM-DATA");
191 if (!msg)
192 return -ENOMEM;
193
194 frame = (struct gsm_data_frame *)msg->data;
195 memset(frame, 0, sizeof(struct gsm_data_frame));
196 data = frame->data;
197 data[0] = 0xd << 4;
198 /* reassemble d-bits */
199 i = 0; /* counts bits */
200 j = 4; /* counts output bits */
201 k = gsm_fr_map[0]-1; /* current number bit in element */
202 l = 0; /* counts element bits */
203 o = 0; /* offset input bits */
204 while (i < 260) {
205 data[j/8] |= (tf.d_bits[k+o] << (7-(j%8)));
206 if (--k < 0) {
207 o += gsm_fr_map[l];
208 k = gsm_fr_map[++l]-1;
209 }
210 i++;
211 j++;
212 }
213 frame->msg_type = GSM_TCHF_FRAME;
214 frame->callref = ue->callref;
215 trau_tx_to_mncc(ue->net, msg);
216
217 return 0;
218 }
219
220 mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
221 if (!mx)
222 return -EINVAL;
223
224 trau_frame_up2down(&tf);
225 encode_trau_frame(trau_bits_out, &tf);
226
227 /* and send it to the muxer */
228 return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out,
229 TRAU_FRAME_BITS);
230}
231
232/* add receiver instance for lchan and callref */
Jonathan Santos5a45b152011-08-17 15:33:57 -0400233int trau_recv_lchan(struct gsm_lchan *lchan, uint32_t callref)
Jonathan Santos03fd8d02011-05-25 13:54:02 -0400234{
235 struct gsm_e1_subslot *src_ss;
236 struct upqueue_entry *ue;
237
238 ue = talloc(tall_upq_ctx, struct upqueue_entry);
239 if (!ue)
240 return -ENOMEM;
241
242 src_ss = &lchan->ts->e1_link;
243
244 DEBUGP(DCC, "Setting up TRAU receiver (e1=%u,ts=%u,ss=%u) "
245 "and (callref 0x%x)\n",
246 src_ss->e1_nr, src_ss->e1_ts, src_ss->e1_ts_ss,
247 callref);
248
249 /* make sure to get rid of any stale old mappings */
250 trau_mux_unmap(src_ss, callref);
251
252 memcpy(&ue->src, src_ss, sizeof(ue->src));
253 ue->net = lchan->ts->trx->bts->network;
254 ue->callref = callref;
255 llist_add(&ue->list, &ss_upqueue);
256
257 return 0;
258}
259
260int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame)
261{
Jonathan Santos5a45b152011-08-17 15:33:57 -0400262 uint8_t trau_bits_out[TRAU_FRAME_BITS];
Jonathan Santos03fd8d02011-05-25 13:54:02 -0400263 struct gsm_e1_subslot *dst_e1_ss = &lchan->ts->e1_link;
264 struct subch_mux *mx;
265 int i, j, k, l, o;
266 unsigned char *data = frame->data;
267 struct decoded_trau_frame tf;
268
269 mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
270 if (!mx)
271 return -EINVAL;
272
273 switch (frame->msg_type) {
274 case GSM_TCHF_FRAME:
275 /* set c-bits and t-bits */
276 tf.c_bits[0] = 1;
277 tf.c_bits[1] = 1;
278 tf.c_bits[2] = 1;
279 tf.c_bits[3] = 0;
280 tf.c_bits[4] = 0;
281 memset(&tf.c_bits[5], 0, 6);
282 memset(&tf.c_bits[11], 1, 10);
283 memset(&tf.t_bits[0], 1, 4);
284 /* reassemble d-bits */
285 i = 0; /* counts bits */
286 j = 4; /* counts input bits */
287 k = gsm_fr_map[0]-1; /* current number bit in element */
288 l = 0; /* counts element bits */
289 o = 0; /* offset output bits */
290 while (i < 260) {
291 tf.d_bits[k+o] = (data[j/8] >> (7-(j%8))) & 1;
292 if (--k < 0) {
293 o += gsm_fr_map[l];
294 k = gsm_fr_map[++l]-1;
295 }
296 i++;
297 j++;
298 }
299 break;
300 default:
301 DEBUGPC(DMUX, "unsupported message type %d\n",
302 frame->msg_type);
303 return -EINVAL;
304 }
305
306 encode_trau_frame(trau_bits_out, &tf);
307
308 /* and send it to the muxer */
309 return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out,
310 TRAU_FRAME_BITS);
311}