blob: 9272ac04f73bf691fcddd5d025c35b0f73822aaa [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>
Alexander Huemer45a00142011-09-06 00:09:49 +020032#include <openbsc/trau_upqueue.h>
Harald Welte59b04682009-06-10 05:40:52 +080033
Holger Hans Peter Freyther16e59db2011-10-26 19:01:57 +020034/* this corresponds to the bit-lengths of the individual codec
Harald Welte9eb9b322011-07-24 02:18:13 +020035 * parameters as indicated in Table 1.1 of TS 06.10 */
36static const uint8_t gsm_fr_map[] = {
Harald Welte3971ad52009-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 Welte59b04682009-06-10 05:40:52 +080049struct map_entry {
50 struct llist_head list;
51 struct gsm_e1_subslot src, dst;
52};
53
54struct upqueue_entry {
55 struct llist_head list;
56 struct gsm_network *net;
57 struct gsm_e1_subslot src;
Holger Hans Peter Freyther7eb8a9a2011-04-18 17:04:00 +020058 uint32_t callref;
Harald Welte59b04682009-06-10 05:40:52 +080059};
60
61static LLIST_HEAD(ss_map);
62static LLIST_HEAD(ss_upqueue);
63
Harald Welte (local)8751ee92009-08-15 02:30:58 +020064void *tall_map_ctx, *tall_upq_ctx;
Harald Weltea8379772009-06-20 22:36:41 +020065
Harald Welte59b04682009-06-10 05:40:52 +080066/* 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 Weltea8379772009-06-20 22:36:41 +020070 struct map_entry *me;
71
Harald Weltea8379772009-06-20 22:36:41 +020072 me = talloc(tall_map_ctx, struct map_entry);
Harald Weltecf2ec4a2009-12-17 23:10:46 +010073 if (!me) {
Pablo Neira Ayuso42e41df2011-08-17 22:44:07 +020074 LOGP(DLMIB, LOGL_FATAL, "Out of memory\n");
Harald Welte59b04682009-06-10 05:40:52 +080075 return -ENOMEM;
Harald Weltecf2ec4a2009-12-17 23:10:46 +010076 }
Harald Welte59b04682009-06-10 05:40:52 +080077
78 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
83 /* make sure to get rid of any stale old mappings */
84 trau_mux_unmap(src, 0);
85 trau_mux_unmap(dst, 0);
86
87 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
94int 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
106/* unmap one particular subslot from another subslot */
Holger Hans Peter Freyther7eb8a9a2011-04-18 17:04:00 +0200107int trau_mux_unmap(const struct gsm_e1_subslot *ss, uint32_t callref)
Harald Welte59b04682009-06-10 05:40:52 +0800108{
109 struct map_entry *me, *me2;
110 struct upqueue_entry *ue, *ue2;
111
112 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);
127 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
148/* 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 Freyther7eb8a9a2011-04-18 17:04:00 +0200161static const uint8_t c_bits_check[] = { 0, 0, 0, 1, 0 };
Harald Welte3971ad52009-12-19 22:23:05 +0100162
Harald Welte59b04682009-06-10 05:40:52 +0800163/* we get called by subchan_demux */
164int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
Holger Hans Peter Freyther7eb8a9a2011-04-18 17:04:00 +0200165 const uint8_t *trau_bits, int num_bits)
Harald Welte59b04682009-06-10 05:40:52 +0800166{
167 struct decoded_trau_frame tf;
Holger Hans Peter Freyther7eb8a9a2011-04-18 17:04:00 +0200168 uint8_t trau_bits_out[TRAU_FRAME_BITS];
Harald Welte59b04682009-06-10 05:40:52 +0800169 struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss);
170 struct subch_mux *mx;
Harald Welte03740842009-06-10 23:11:52 +0800171 struct upqueue_entry *ue;
Harald Welte59b04682009-06-10 05:40:52 +0800172 int rc;
173
174 /* decode TRAU, change it to downlink, re-encode */
175 rc = decode_trau_frame(&tf, trau_bits);
176 if (rc)
177 return rc;
178
Harald Welte03740842009-06-10 23:11:52 +0800179 if (!dst_e1_ss) {
Harald Welte3971ad52009-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 Welte03740842009-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 Eversbergbd107a42009-12-21 01:18:44 +0100189 if (memcmp(tf.c_bits, c_bits_check, sizeof(c_bits_check)))
Pablo Neira Ayuso42e41df2011-08-17 22:44:07 +0200190 DEBUGPC(DLMUX, "illegal trau (C1-C5) %s\n",
Pablo Neira Ayusob1d5a692011-05-07 12:12:48 +0200191 osmo_hexdump(tf.c_bits, sizeof(c_bits_check)));
Harald Welte3971ad52009-12-19 22:23:05 +0100192 msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33,
193 "GSM-DATA");
Harald Welte03740842009-06-10 23:11:52 +0800194 if (!msg)
195 return -ENOMEM;
Harald Welte3971ad52009-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 Welte03740842009-06-10 23:11:52 +0800217 frame->callref = ue->callref;
Pablo Neira Ayuso93097352011-08-10 02:56:58 +0200218 msgb_put(msg, sizeof(struct gsm_data_frame) + 33);
Harald Welte4f272cc2011-03-03 23:29:05 +0100219 trau_tx_to_mncc(ue->net, msg);
Harald Welte03740842009-06-10 23:11:52 +0800220
221 return 0;
222 }
Harald Welte59b04682009-06-10 05:40:52 +0800223
224 mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
225 if (!mx)
226 return -EINVAL;
227
228 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}
235
Pablo Neira Ayuso42e41df2011-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 Welte59b04682009-06-10 05:40:52 +0800250/* add receiver instance for lchan and callref */
Holger Hans Peter Freyther7eb8a9a2011-04-18 17:04:00 +0200251int trau_recv_lchan(struct gsm_lchan *lchan, uint32_t callref)
Harald Welte59b04682009-06-10 05:40:52 +0800252{
253 struct gsm_e1_subslot *src_ss;
Harald Weltea8379772009-06-20 22:36:41 +0200254 struct upqueue_entry *ue;
Harald Welte59b04682009-06-10 05:40:52 +0800255
Harald Weltea8379772009-06-20 22:36:41 +0200256 ue = talloc(tall_upq_ctx, struct upqueue_entry);
Harald Welte59b04682009-06-10 05:40:52 +0800257 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 Welte3971ad52009-12-19 22:23:05 +0100278int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame)
Harald Welte59b04682009-06-10 05:40:52 +0800279{
Holger Hans Peter Freyther7eb8a9a2011-04-18 17:04:00 +0200280 uint8_t trau_bits_out[TRAU_FRAME_BITS];
Harald Welte59b04682009-06-10 05:40:52 +0800281 struct gsm_e1_subslot *dst_e1_ss = &lchan->ts->e1_link;
282 struct subch_mux *mx;
Harald Welte3971ad52009-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 Welte59b04682009-06-10 05:40:52 +0800286
287 mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
288 if (!mx)
289 return -EINVAL;
290
Harald Welte3971ad52009-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 Ayuso42e41df2011-08-17 22:44:07 +0200319 DEBUGPC(DLMUX, "unsupported message type %d\n",
Harald Welte3971ad52009-12-19 22:23:05 +0100320 frame->msg_type);
321 return -EINVAL;
322 }
323
324 encode_trau_frame(trau_bits_out, &tf);
Harald Welte59b04682009-06-10 05:40:52 +0800325
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}