blob: d8bf29f169d8f64a6516482b737d722b99bd64ba [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 Welte54bd94d2009-01-05 19:00:01 +000030
Harald Welte1fa60c82009-02-09 18:13:26 +000031static inline void append_bit(struct demux_subch *sch, u_int8_t bit)
Harald Welte54bd94d2009-01-05 19:00:01 +000032{
33 sch->out_bitbuf[sch->out_idx++] = bit;
34}
35
36#define SYNC_HDR_BITS 16
37static const u_int8_t nullbytes[SYNC_HDR_BITS];
38
39/* check if we have just completed the 16 bit zero sync header,
40 * in accordance with GSM TS 08.60 Chapter 4.8.1 */
Harald Welte1fa60c82009-02-09 18:13:26 +000041static int sync_hdr_complete(struct demux_subch *sch)
Harald Welte54bd94d2009-01-05 19:00:01 +000042{
43 int rc;
44 int bits_at_end = 0;
45 int bits_at_front;
46
47 if (sch->out_idx < SYNC_HDR_BITS)
48 bits_at_end = SYNC_HDR_BITS - sch->out_idx;
49 bits_at_front = sch->out_idx;
50
51 if (bits_at_end) {
52 rc = memcmp(sch->out_bitbuf + sizeof(sch->out_bitbuf) - bits_at_end,
53 nullbytes, bits_at_end);
54 if (rc)
55 return 0;
56 }
57 rc = memcmp(sch->out_bitbuf + sch->out_idx, nullbytes,
58 bits_at_front);
59 if (rc)
60 return 0;
61
62 return 1;
63}
64
65/* resynchronize to current location */
Harald Welte1fa60c82009-02-09 18:13:26 +000066static void resync_to_here(struct demux_subch *sch)
Harald Welte54bd94d2009-01-05 19:00:01 +000067{
68#if 0
69 u_int8_t tmp[TRAU_FRAME_BITS];
70 int sync_hdr_start = sch->out_idx - SYNC_HDR_BITS;
71 int bytes_at_end;
72
73 /* first make copy of old bitbuf */
74 memcpy(tmp, sch->out_bitbuf, sizeof(tmp));
75
76 if (sync_hdr_start < 0)
77 sync_hdr_start += TRAU_FRAME_BITS;
78
79 bytes_at_end = TRAU_FRAME_BITS - sync_hdr_start;
80
81 /* copy part after sync_hdr_start */
82 memcpy(sch->out_bitbuf, tmp + sync_hdr_start, bytes_at_end);
83
84 /* copy part before sync_hdr_start */
85 memcpy(sch->out_bitbuf + bytes_at_end, tmp,
86 SYNC_HDR_BITS - bytes_at_end);
87#else
88 memset(sch->out_bitbuf, 0, SYNC_HDR_BITS);
89#endif
90
91 /* set index in a way that we can continue receiving bits after
92 * the end of the SYNC header */
93 sch->out_idx = SYNC_HDR_BITS;
94}
95
Harald Weltece281c02009-01-05 20:14:14 +000096int subch_demux_init(struct subch_demux *dmx)
97{
98 int i;
99
100 dmx->chan_activ = 0;
101 for (i = 0; i < NR_SUBCH; i++) {
Harald Welte1fa60c82009-02-09 18:13:26 +0000102 struct demux_subch *sch = &dmx->subch[i];
Harald Weltece281c02009-01-05 20:14:14 +0000103 sch->out_idx = 0;
104 memset(sch->out_bitbuf, 0xff, sizeof(sch->out_bitbuf));
105 }
106 return 0;
107}
108
Harald Welte54bd94d2009-01-05 19:00:01 +0000109/* input some arbitrary (modulo 4) number of bytes of a 64k E1 channel,
110 * split it into the 16k subchannels */
111int subch_demux_in(struct subch_demux *dmx, u_int8_t *data, int len)
112{
113 int i, c;
114
115 /* we avoid partially filled bytes in outbuf */
116 if (len % 4)
117 return -EINVAL;
118
119 for (i = 0; i < len; i++) {
120 u_int8_t inbyte = data[i];
121
122 for (c = 0; c < NR_SUBCH; c++) {
Harald Welte1fa60c82009-02-09 18:13:26 +0000123 struct demux_subch *sch = &dmx->subch[c];
Harald Welte54bd94d2009-01-05 19:00:01 +0000124 u_int8_t bit;
125
126 /* ignore inactive subchannels */
127 if (!(dmx->chan_activ & (1 << c)))
128 continue;
129
130 /* two bits for each subchannel */
131 if ((inbyte >> (c * 2)) & 0x01)
132 bit = 1;
133 else
134 bit = 0;
135 append_bit(sch, bit);
136
137 if (sync_hdr_complete(sch))
138 resync_to_here(sch);
139
140 if ((inbyte >> (c * 2)) & 0x02)
141 bit = 1;
142 else
143 bit = 0;
144 append_bit(sch, bit);
145
146 if (sync_hdr_complete(sch))
147 resync_to_here(sch);
148
149 /* FIXME: verify the first bit in octet 2, 4, 6, ...
150 * according to TS 08.60 4.8.1 */
151
152 /* once we have reached TRAU_FRAME_BITS, call
153 * the TRAU frame handler callback function */
154 if (sch->out_idx >= TRAU_FRAME_BITS) {
155 dmx->out_cb(dmx, c, sch->out_bitbuf,
156 sch->out_idx, dmx->data);
157 sch->out_idx = 0;
158 }
159 }
160 }
161 return i;
162}
163
164int subch_demux_activate(struct subch_demux *dmx, int subch)
165{
166 if (subch >= NR_SUBCH)
167 return -EINVAL;
168
169 dmx->chan_activ |= (1 << subch);
170 return 0;
171}
172
173int subch_demux_deactivate(struct subch_demux *dmx, int subch)
174{
175 if (subch >= NR_SUBCH)
176 return -EINVAL;
177
178 dmx->chan_activ &= ~(1 << subch);
179 return 0;
180}
Harald Welte1fa60c82009-02-09 18:13:26 +0000181
182/* MULTIPLEXER */
183
184static int alloc_add_idle_frame(struct subch_mux *mx, int sch_nr)
185{
186 /* FIXME: allocate and initialize with idle pattern */
187 return subchan_mux_enqueue(mx, sch_nr, trau_idle_frame(),
188 TRAU_FRAME_BITS);
189}
190
191/* return the requested number of bits from the specified subchannel */
192static int get_subch_bits(struct subch_mux *mx, int subch,
193 u_int8_t *bits, int num_requested)
194{
195 struct mux_subch *sch = &mx->subch[subch];
196 int num_bits = 0;
197
198 while (num_bits < num_requested) {
199 struct subch_txq_entry *txe;
200 int num_bits_left;
201 int num_bits_thistime;
202
203 /* make sure we have a valid entry at top of tx queue.
204 * if not, add an idle frame */
Holger Freyther3630eab2009-02-09 21:05:56 +0000205 if (llist_empty(&sch->tx_queue))
Harald Welte1fa60c82009-02-09 18:13:26 +0000206 alloc_add_idle_frame(mx, subch);
207
208 if (llist_empty(&sch->tx_queue))
209 return -EIO;
210
Holger Freyther3630eab2009-02-09 21:05:56 +0000211 txe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list);
Harald Welte1fa60c82009-02-09 18:13:26 +0000212 num_bits_left = txe->bit_len - txe->next_bit;
213
214 if (num_bits_left < num_requested)
215 num_bits_thistime = num_bits_left;
216 else
217 num_bits_thistime = num_requested;
218
219 /* pull the bits from the txe */
220 memcpy(bits + num_bits, txe->bits + txe->next_bit, num_requested);
221 txe->next_bit += num_bits_thistime;
222
223 /* free the tx_queue entry if it is fully consumed */
224 if (txe->next_bit >= txe->bit_len) {
225 llist_del(&txe->list);
226 free(txe);
227 }
228
229 /* increment global number of bits dequeued */
230 num_bits += num_bits_thistime;
231 }
232
233 return num_requested;
234}
235
236/* compact an array of 8 single-bit bytes into one byte of 8 bits */
237static u_int8_t compact_bits(u_int8_t *bits)
238{
239 u_int8_t ret = 0;
240 int i;
241
242 for (i = 0; i < 8; i++)
243 ret |= (bits[i] ? 1 : 0) << i;
244
245 return ret;
246}
247
248/* obtain a single output byte from the subchannel muxer */
249static int mux_output_byte(struct subch_mux *mx, u_int8_t *byte)
250{
251 u_int8_t bits[8];
252 int rc;
253
254 /* combine two bits of every subchan */
255 rc = get_subch_bits(mx, 0, &bits[0], 2);
256 rc = get_subch_bits(mx, 1, &bits[2], 2);
257 rc = get_subch_bits(mx, 2, &bits[4], 2);
258 rc = get_subch_bits(mx, 3, &bits[6], 2);
259
260 if (!rc)
261 *byte = compact_bits(bits);
262
263 return rc;
264}
265
266/* Request the output of some muxed bytes from the subchan muxer */
267int subchan_mux_out(struct subch_mux *mx, u_int8_t *data, int len)
268{
269 int i;
270
271 for (i = 0; i < len; i++) {
272 int rc;
273 rc = mux_output_byte(mx, &data[i]);
274 if (rc < 0)
275 break;
276 }
277 return i;
278}
279
280/* enqueue some data into the tx_queue of a given subchannel */
281int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const u_int8_t *data,
282 int len)
283{
284 struct mux_subch *sch = &mx->subch[s_nr];
285 struct subch_txq_entry *tqe = malloc(sizeof(*tqe) + len);
286
287 if (!tqe)
288 return -ENOMEM;
289
290 memset(tqe, 0, sizeof(*tqe));
291 tqe->bit_len = len;
292 memcpy(tqe->bits, data, len);
293
Holger Freyther3630eab2009-02-09 21:05:56 +0000294 llist_add_tail(&tqe->list, &sch->tx_queue);
Harald Welte1fa60c82009-02-09 18:13:26 +0000295
296 return 0;
297}
298
299/* initialize one subchannel muxer instance */
300int subchan_mux_init(struct subch_mux *mx)
301{
302 int i;
303
304 memset(mx, 0, sizeof(*mx));
305 for (i = 0; i < NR_SUBCH; i++) {
306 struct mux_subch *sch = &mx->subch[i];
307 INIT_LLIST_HEAD(&sch->tx_queue);
308 }
309
310 return 0;
311}