blob: 368b9853c5ed322cc1a01cc9df899c3e1a6d3c51 [file] [log] [blame]
Harald Welte1fa60c82009-02-09 18:13:26 +00001/* A E1 sub-channel (de)multiplexer with TRAU frame sync */
Harald Welte54bd94d2009-01-05 19:00:01 +00002
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 General Public License as published by
8 * the Free Software Foundation; either version 2 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 General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 */
21
22#include <unistd.h>
23#include <stdlib.h>
24#include <stdio.h>
25#include <string.h>
26#include <errno.h>
27
28#include <openbsc/subchan_demux.h>
Harald Welte1fa60c82009-02-09 18:13:26 +000029#include <openbsc/trau_frame.h>
Harald Welte7eb1f622009-02-18 03:40:58 +000030#include <openbsc/debug.h>
Harald Welte2cf161b2009-06-20 22:36:41 +020031#include <openbsc/talloc.h>
32#include <openbsc/gsm_data.h>
33
34static void *tall_tqe_ctx;
Harald Welte54bd94d2009-01-05 19:00:01 +000035
Harald Welte1fa60c82009-02-09 18:13:26 +000036static inline void append_bit(struct demux_subch *sch, u_int8_t bit)
Harald Welte54bd94d2009-01-05 19:00:01 +000037{
38 sch->out_bitbuf[sch->out_idx++] = bit;
39}
40
41#define SYNC_HDR_BITS 16
42static const u_int8_t nullbytes[SYNC_HDR_BITS];
43
44/* check if we have just completed the 16 bit zero sync header,
45 * in accordance with GSM TS 08.60 Chapter 4.8.1 */
Harald Welte7eb1f622009-02-18 03:40:58 +000046static int sync_hdr_complete(struct demux_subch *sch, u_int8_t bit)
Harald Welte54bd94d2009-01-05 19:00:01 +000047{
Harald Welte7eb1f622009-02-18 03:40:58 +000048 if (bit == 0)
49 sch->consecutive_zeros++;
50 else
51 sch->consecutive_zeros = 0;
Harald Welte54bd94d2009-01-05 19:00:01 +000052
Harald Welte7eb1f622009-02-18 03:40:58 +000053 if (sch->consecutive_zeros >= SYNC_HDR_BITS) {
54 sch->consecutive_zeros = 0;
55 return 1;
Harald Welte54bd94d2009-01-05 19:00:01 +000056 }
Harald Welte7eb1f622009-02-18 03:40:58 +000057
58 return 0;
Harald Welte54bd94d2009-01-05 19:00:01 +000059}
60
61/* resynchronize to current location */
Harald Welte1fa60c82009-02-09 18:13:26 +000062static void resync_to_here(struct demux_subch *sch)
Harald Welte54bd94d2009-01-05 19:00:01 +000063{
Harald Welte54bd94d2009-01-05 19:00:01 +000064 memset(sch->out_bitbuf, 0, SYNC_HDR_BITS);
Harald Welte54bd94d2009-01-05 19:00:01 +000065
66 /* set index in a way that we can continue receiving bits after
67 * the end of the SYNC header */
68 sch->out_idx = SYNC_HDR_BITS;
Harald Weltef1e6f962009-02-19 17:05:13 +000069 sch->in_sync = 1;
Harald Welte54bd94d2009-01-05 19:00:01 +000070}
71
Harald Weltece281c02009-01-05 20:14:14 +000072int subch_demux_init(struct subch_demux *dmx)
73{
74 int i;
75
76 dmx->chan_activ = 0;
77 for (i = 0; i < NR_SUBCH; i++) {
Harald Welte1fa60c82009-02-09 18:13:26 +000078 struct demux_subch *sch = &dmx->subch[i];
Harald Weltece281c02009-01-05 20:14:14 +000079 sch->out_idx = 0;
80 memset(sch->out_bitbuf, 0xff, sizeof(sch->out_bitbuf));
81 }
82 return 0;
83}
84
Harald Welte54bd94d2009-01-05 19:00:01 +000085/* input some arbitrary (modulo 4) number of bytes of a 64k E1 channel,
86 * split it into the 16k subchannels */
87int subch_demux_in(struct subch_demux *dmx, u_int8_t *data, int len)
88{
89 int i, c;
90
91 /* we avoid partially filled bytes in outbuf */
92 if (len % 4)
93 return -EINVAL;
94
95 for (i = 0; i < len; i++) {
96 u_int8_t inbyte = data[i];
97
98 for (c = 0; c < NR_SUBCH; c++) {
Harald Welte1fa60c82009-02-09 18:13:26 +000099 struct demux_subch *sch = &dmx->subch[c];
Harald Welte7eb1f622009-02-18 03:40:58 +0000100 u_int8_t inbits;
Harald Welte54bd94d2009-01-05 19:00:01 +0000101 u_int8_t bit;
102
103 /* ignore inactive subchannels */
104 if (!(dmx->chan_activ & (1 << c)))
105 continue;
106
Harald Welte360f06c2009-04-29 16:21:18 +0000107 inbits = inbyte >> (c << 1);
Harald Welte7eb1f622009-02-18 03:40:58 +0000108
Harald Welte54bd94d2009-01-05 19:00:01 +0000109 /* two bits for each subchannel */
Harald Welte7eb1f622009-02-18 03:40:58 +0000110 if (inbits & 0x01)
Harald Welte54bd94d2009-01-05 19:00:01 +0000111 bit = 1;
112 else
113 bit = 0;
114 append_bit(sch, bit);
115
Harald Welte7eb1f622009-02-18 03:40:58 +0000116 if (sync_hdr_complete(sch, bit))
Harald Welte54bd94d2009-01-05 19:00:01 +0000117 resync_to_here(sch);
118
Harald Welte7eb1f622009-02-18 03:40:58 +0000119 if (inbits & 0x02)
Harald Welte54bd94d2009-01-05 19:00:01 +0000120 bit = 1;
121 else
122 bit = 0;
123 append_bit(sch, bit);
124
Harald Welte7eb1f622009-02-18 03:40:58 +0000125 if (sync_hdr_complete(sch, bit))
Harald Welte54bd94d2009-01-05 19:00:01 +0000126 resync_to_here(sch);
127
128 /* FIXME: verify the first bit in octet 2, 4, 6, ...
129 * according to TS 08.60 4.8.1 */
130
131 /* once we have reached TRAU_FRAME_BITS, call
132 * the TRAU frame handler callback function */
133 if (sch->out_idx >= TRAU_FRAME_BITS) {
Harald Weltef1e6f962009-02-19 17:05:13 +0000134 if (sch->in_sync) {
135 dmx->out_cb(dmx, c, sch->out_bitbuf,
Harald Welte54bd94d2009-01-05 19:00:01 +0000136 sch->out_idx, dmx->data);
Harald Weltef1e6f962009-02-19 17:05:13 +0000137 sch->in_sync = 0;
138 }
Harald Welte54bd94d2009-01-05 19:00:01 +0000139 sch->out_idx = 0;
140 }
141 }
142 }
143 return i;
144}
145
146int subch_demux_activate(struct subch_demux *dmx, int subch)
147{
148 if (subch >= NR_SUBCH)
149 return -EINVAL;
150
151 dmx->chan_activ |= (1 << subch);
152 return 0;
153}
154
155int subch_demux_deactivate(struct subch_demux *dmx, int subch)
156{
157 if (subch >= NR_SUBCH)
158 return -EINVAL;
159
160 dmx->chan_activ &= ~(1 << subch);
161 return 0;
162}
Harald Welte1fa60c82009-02-09 18:13:26 +0000163
164/* MULTIPLEXER */
165
166static int alloc_add_idle_frame(struct subch_mux *mx, int sch_nr)
167{
Harald Welte7eb1f622009-02-18 03:40:58 +0000168 /* allocate and initialize with idle pattern */
Harald Welte1fa60c82009-02-09 18:13:26 +0000169 return subchan_mux_enqueue(mx, sch_nr, trau_idle_frame(),
170 TRAU_FRAME_BITS);
171}
172
173/* return the requested number of bits from the specified subchannel */
174static int get_subch_bits(struct subch_mux *mx, int subch,
175 u_int8_t *bits, int num_requested)
176{
177 struct mux_subch *sch = &mx->subch[subch];
178 int num_bits = 0;
179
180 while (num_bits < num_requested) {
181 struct subch_txq_entry *txe;
182 int num_bits_left;
183 int num_bits_thistime;
184
185 /* make sure we have a valid entry at top of tx queue.
186 * if not, add an idle frame */
Holger Freyther3630eab2009-02-09 21:05:56 +0000187 if (llist_empty(&sch->tx_queue))
Harald Welte1fa60c82009-02-09 18:13:26 +0000188 alloc_add_idle_frame(mx, subch);
189
190 if (llist_empty(&sch->tx_queue))
191 return -EIO;
192
Holger Freyther3630eab2009-02-09 21:05:56 +0000193 txe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list);
Harald Welte1fa60c82009-02-09 18:13:26 +0000194 num_bits_left = txe->bit_len - txe->next_bit;
195
196 if (num_bits_left < num_requested)
197 num_bits_thistime = num_bits_left;
198 else
199 num_bits_thistime = num_requested;
200
201 /* pull the bits from the txe */
Harald Welte7eb1f622009-02-18 03:40:58 +0000202 memcpy(bits + num_bits, txe->bits + txe->next_bit, num_bits_thistime);
Harald Welte1fa60c82009-02-09 18:13:26 +0000203 txe->next_bit += num_bits_thistime;
204
205 /* free the tx_queue entry if it is fully consumed */
206 if (txe->next_bit >= txe->bit_len) {
207 llist_del(&txe->list);
Harald Welte2cf161b2009-06-20 22:36:41 +0200208 talloc_free(txe);
Harald Welte1fa60c82009-02-09 18:13:26 +0000209 }
210
211 /* increment global number of bits dequeued */
212 num_bits += num_bits_thistime;
213 }
214
215 return num_requested;
216}
217
218/* compact an array of 8 single-bit bytes into one byte of 8 bits */
Harald Welte7eb1f622009-02-18 03:40:58 +0000219static u_int8_t compact_bits(const u_int8_t *bits)
Harald Welte1fa60c82009-02-09 18:13:26 +0000220{
221 u_int8_t ret = 0;
222 int i;
223
224 for (i = 0; i < 8; i++)
225 ret |= (bits[i] ? 1 : 0) << i;
226
227 return ret;
228}
229
230/* obtain a single output byte from the subchannel muxer */
231static int mux_output_byte(struct subch_mux *mx, u_int8_t *byte)
232{
233 u_int8_t bits[8];
234 int rc;
235
236 /* combine two bits of every subchan */
Harald Welte360f06c2009-04-29 16:21:18 +0000237 rc = get_subch_bits(mx, 0, &bits[0], 2);
238 rc = get_subch_bits(mx, 1, &bits[2], 2);
239 rc = get_subch_bits(mx, 2, &bits[4], 2);
240 rc = get_subch_bits(mx, 3, &bits[6], 2);
Harald Welte1fa60c82009-02-09 18:13:26 +0000241
Harald Welte7eb1f622009-02-18 03:40:58 +0000242 *byte = compact_bits(bits);
Harald Welte1fa60c82009-02-09 18:13:26 +0000243
244 return rc;
245}
246
247/* Request the output of some muxed bytes from the subchan muxer */
248int subchan_mux_out(struct subch_mux *mx, u_int8_t *data, int len)
249{
250 int i;
251
252 for (i = 0; i < len; i++) {
253 int rc;
254 rc = mux_output_byte(mx, &data[i]);
255 if (rc < 0)
256 break;
257 }
258 return i;
259}
260
Harald Welteb49fe962009-02-22 22:28:19 +0000261static int llist_len(struct llist_head *head)
262{
263 struct llist_head *entry;
264 int i = 0;
265
266 llist_for_each(entry, head)
267 i++;
268
269 return i;
270}
271
272/* evict the 'num_evict' number of oldest entries in the queue */
273static void tx_queue_evict(struct mux_subch *sch, int num_evict)
274{
275 struct subch_txq_entry *tqe;
276 int i;
277
278 for (i = 0; i < num_evict; i++) {
279 if (llist_empty(&sch->tx_queue))
280 return;
281
282 tqe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list);
283 llist_del(&tqe->list);
Harald Welte2cf161b2009-06-20 22:36:41 +0200284 talloc_free(tqe);
Harald Welteb49fe962009-02-22 22:28:19 +0000285 }
286}
287
Harald Welte1fa60c82009-02-09 18:13:26 +0000288/* enqueue some data into the tx_queue of a given subchannel */
289int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const u_int8_t *data,
290 int len)
291{
292 struct mux_subch *sch = &mx->subch[s_nr];
Harald Welteb49fe962009-02-22 22:28:19 +0000293 int list_len = llist_len(&sch->tx_queue);
Harald Welte470ec292009-06-26 20:25:23 +0200294 struct subch_txq_entry *tqe = talloc_zero_size(tall_tqe_ctx,
295 sizeof(*tqe) + len);
Harald Welte1fa60c82009-02-09 18:13:26 +0000296 if (!tqe)
297 return -ENOMEM;
298
Harald Welte1fa60c82009-02-09 18:13:26 +0000299 tqe->bit_len = len;
300 memcpy(tqe->bits, data, len);
301
Harald Welteb49fe962009-02-22 22:28:19 +0000302 if (list_len > 2)
303 tx_queue_evict(sch, list_len-2);
304
Holger Freyther3630eab2009-02-09 21:05:56 +0000305 llist_add_tail(&tqe->list, &sch->tx_queue);
Harald Welte1fa60c82009-02-09 18:13:26 +0000306
307 return 0;
308}
309
310/* initialize one subchannel muxer instance */
311int subchan_mux_init(struct subch_mux *mx)
312{
313 int i;
314
315 memset(mx, 0, sizeof(*mx));
316 for (i = 0; i < NR_SUBCH; i++) {
317 struct mux_subch *sch = &mx->subch[i];
318 INIT_LLIST_HEAD(&sch->tx_queue);
319 }
320
321 return 0;
322}
Harald Welte7bfc2672009-07-28 00:41:45 +0200323
324static __attribute__((constructor)) void on_dso_load_ss_demux(void)
325{
326 tall_tqe_ctx = talloc_named_const(tall_bsc_ctx, 1,
327 "subch_txq_entry");
328}