blob: 50cb56e9e6754535c15d592edb5749a8239a8d75 [file] [log] [blame]
Harald Welteb795f032020-05-14 11:42:53 +02001/*! \file i460_mux.c
2 * ITU-T I.460 sub-channel multiplexer + demultiplexer */
3/*
4 * (C) 2020 by Harald Welte <laforge@gnumonks.org>
5 *
6 * SPDX-License-Identifier: GPL-2.0+
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 * MA 02110-1301, USA.
22 */
23
24#include <errno.h>
25
26#include <osmocom/core/bits.h>
27#include <osmocom/core/utils.h>
28#include <osmocom/core/msgb.h>
29#include <osmocom/gsm/i460_mux.h>
30
31/* count the number of sub-channels in this I460 slot */
32static int osmo_i460_subchan_count(struct osmo_i460_timeslot *ts)
33{
34 int i, num_used = 0;
35
36 for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
37 if (ts->schan[i].rate != OSMO_I460_RATE_NONE)
38 num_used++;
39 }
40
41 return num_used;
42}
43
44/* does this channel have no sub-streams (single 64k subchannel)? */
45static bool osmo_i460_has_single_64k_schan(struct osmo_i460_timeslot *ts)
46{
47 if (osmo_i460_subchan_count(ts) != 1)
48 return false;
49
50 if (ts->schan[0].rate != OSMO_I460_RATE_64k)
51 return false;
52
53 return true;
54}
55
56/***********************************************************************
57 * Demultiplexer
58 ***********************************************************************/
59
60/* append a single bit to a sub-channel */
61static void demux_subchan_append_bit(struct osmo_i460_subchan *schan, uint8_t bit)
62{
63 struct osmo_i460_subchan_demux *demux = &schan->demux;
64
65 OSMO_ASSERT(demux->out_bitbuf);
66 OSMO_ASSERT(demux->out_idx < demux->out_bitbuf_size);
67
68 demux->out_bitbuf[demux->out_idx++] = bit ? 1 : 0;
69
70 if (demux->out_idx >= demux->out_bitbuf_size) {
71 if (demux->out_cb_bits)
72 demux->out_cb_bits(demux->user_data, demux->out_bitbuf, demux->out_idx);
73 else {
74 /* pack bits into bytes */
75 OSMO_ASSERT((demux->out_idx % 8) == 0);
76 unsigned int num_bytes = demux->out_idx / 8;
77 uint8_t bytes[num_bytes];
78 osmo_ubit2pbit(bytes, demux->out_bitbuf, demux->out_idx);
79 demux->out_cb_bytes(demux->user_data, bytes, num_bytes);
80 }
81 demux->out_idx = 0;
82 }
83}
84
85/* extract those bits relevant to this schan of each byte in 'data' */
86static void demux_subchan_extract_bits(struct osmo_i460_subchan *schan, const uint8_t *data, size_t data_len)
87{
88 int i;
89
90 for (i = 0; i < data_len; i++) {
91 uint8_t inbyte = data[i];
92 uint8_t inbits = inbyte >> schan->bit_offset;
93
94 /* extract the bits relevant to the given schan */
95 switch (schan->rate) {
96 case OSMO_I460_RATE_8k:
97 demux_subchan_append_bit(schan, inbits & 0x01);
98 break;
99 case OSMO_I460_RATE_16k:
100 demux_subchan_append_bit(schan, inbits & 0x01);
101 demux_subchan_append_bit(schan, inbits & 0x02);
102 break;
103 case OSMO_I460_RATE_32k:
104 demux_subchan_append_bit(schan, inbits & 0x01);
105 demux_subchan_append_bit(schan, inbits & 0x02);
106 demux_subchan_append_bit(schan, inbits & 0x04);
107 demux_subchan_append_bit(schan, inbits & 0x08);
108 break;
109 case OSMO_I460_RATE_64k:
110 demux_subchan_append_bit(schan, inbits & 0x01);
111 demux_subchan_append_bit(schan, inbits & 0x02);
112 demux_subchan_append_bit(schan, inbits & 0x04);
113 demux_subchan_append_bit(schan, inbits & 0x08);
114 demux_subchan_append_bit(schan, inbits & 0x10);
115 demux_subchan_append_bit(schan, inbits & 0x20);
116 demux_subchan_append_bit(schan, inbits & 0x40);
117 demux_subchan_append_bit(schan, inbits & 0x80);
118 break;
119 default:
120 OSMO_ASSERT(0);
121 }
122 }
123}
124
125/*! Data from E1 timeslot into de-multiplexer
126 * \param[in] ts timeslot state
127 * \param[in] data input data bytes as received from E1/T1
128 * \param[in] data_len length of data in bytes */
129void osmo_i460_demux_in(struct osmo_i460_timeslot *ts, const uint8_t *data, size_t data_len)
130{
131 struct osmo_i460_subchan *schan;
132 struct osmo_i460_subchan_demux *demux;
133 int i;
134
135 /* fast path if entire 64k slot is used */
136 if (osmo_i460_has_single_64k_schan(ts)) {
137 schan = &ts->schan[0];
138 demux = &schan->demux;
139 if (demux->out_cb_bytes)
140 demux->out_cb_bytes(demux->user_data, data, data_len);
141 else {
142 ubit_t bits[data_len*8];
143 osmo_pbit2ubit(bits, data, data_len*8);
144 demux->out_cb_bits(demux->user_data, bits, data_len*8);
145 }
146 return;
147 }
148
149 /* Slow path iterating over all lchans */
150 for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
151 schan = &ts->schan[i];
152 if (schan->rate == OSMO_I460_RATE_NONE)
153 continue;
154 demux_subchan_extract_bits(schan, data, data_len);
155 }
156}
157
158
159/***********************************************************************
160 * Multiplexer
161 ***********************************************************************/
162
163/*! enqueue a to-be-transmitted message buffer containing unpacked bits */
164void osmo_i460_mux_enqueue(struct osmo_i460_subchan *schan, struct msgb *msg)
165{
166 OSMO_ASSERT(msgb_length(msg) > 0);
167 msgb_enqueue(&schan->mux.tx_queue, msg);
168}
169
170/* mux: pull the next bit out of the given sub-channel */
171static ubit_t mux_schan_provide_bit(struct osmo_i460_subchan *schan)
172{
173 struct osmo_i460_subchan_mux *mux = &schan->mux;
174 struct msgb *msg;
175 ubit_t bit;
176
177 /* if we don't have anything to transmit, return '1' bits */
178 if (llist_empty(&mux->tx_queue))
179 return 0x01;
180
181 msg = llist_entry(mux->tx_queue.next, struct msgb, list);
182 bit = msgb_pull_u8(msg);
183
184 /* free msgb if we have pulled the last bit */
185 if (msgb_length(msg) <= 0) {
186 llist_del(&msg->list);
187 talloc_free(msg);
188 }
189
190 return bit;
191}
192
193/*! provide one byte with the subchan-specific bits of given sub-channel.
194 * \param[in] schan sub-channel that is to provide bits
195 * \parma[out] mask bitmask of those bits filled in
196 * \returns bits of given sub-channel */
197static uint8_t mux_subchan_provide_bits(struct osmo_i460_subchan *schan, uint8_t *mask)
198{
199 uint8_t outbits = 0;
200 uint8_t outmask;
201
202 switch (schan->rate) {
203 case OSMO_I460_RATE_8k:
204 outbits = mux_schan_provide_bit(schan);
205 outmask = 0x01;
206 break;
207 case OSMO_I460_RATE_16k:
208 outbits |= mux_schan_provide_bit(schan) << 1;
209 outbits |= mux_schan_provide_bit(schan) << 0;
210 outmask = 0x03;
211 break;
212 case OSMO_I460_RATE_32k:
213 outbits |= mux_schan_provide_bit(schan) << 3;
214 outbits |= mux_schan_provide_bit(schan) << 2;
215 outbits |= mux_schan_provide_bit(schan) << 1;
216 outbits |= mux_schan_provide_bit(schan) << 0;
217 outmask = 0x0F;
218 break;
219 case OSMO_I460_RATE_64k:
220 outbits |= mux_schan_provide_bit(schan) << 7;
221 outbits |= mux_schan_provide_bit(schan) << 6;
222 outbits |= mux_schan_provide_bit(schan) << 5;
223 outbits |= mux_schan_provide_bit(schan) << 4;
224 outbits |= mux_schan_provide_bit(schan) << 3;
225 outbits |= mux_schan_provide_bit(schan) << 2;
226 outbits |= mux_schan_provide_bit(schan) << 1;
227 outbits |= mux_schan_provide_bit(schan) << 0;
228 outmask = 0xFF;
229 break;
230 default:
231 OSMO_ASSERT(0);
232 }
233 *mask = outmask << schan->bit_offset;
234 return outbits << schan->bit_offset;
235}
236
237/* provide one byte of multiplexed I.460 bits */
238static uint8_t mux_timeslot_provide_bits(struct osmo_i460_timeslot *ts)
239{
240 int i, count = 0;
241 uint8_t ret = 0xff; /* unused bits must be '1' as per I.460 */
242
243 for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
244 struct osmo_i460_subchan *schan = &ts->schan[i];
245 uint8_t bits, mask;
246
247 if (schan->rate == OSMO_I460_RATE_NONE)
248 continue;
249 count++;
250 bits = mux_subchan_provide_bits(schan, &mask);
251 ret &= ~mask;
252 ret |= bits;
253 }
254
255 return ret;
256}
257
258
259/*! Data from E1 timeslot into de-multiplexer
260 * \param[in] ts timeslot state
261 * \param[out] out caller-provided buffer where to store generated output bytes
262 * \param[in] out_len number of bytes to be stored at out
263 */
264int osmo_i460_mux_out(struct osmo_i460_timeslot *ts, uint8_t *out, size_t out_len)
265{
266 int i;
267
268 /* fast path if entire 64k slot is used */
269 //if (osmo_i460_has_single_64k_schan(ts)) { }
270
271 for (i = 0; i < out_len; i++)
272 out[i] = mux_timeslot_provide_bits(ts);
273
274 return out_len;
275}
276
277
278/***********************************************************************
279 * Initialization / Control
280 ***********************************************************************/
281
282
283static int alloc_bitbuf(void *ctx, struct osmo_i460_subchan *schan, size_t num_bits)
284{
285 struct osmo_i460_subchan_demux *demux = &schan->demux;
286
287 talloc_free(demux->out_bitbuf);
288 demux->out_bitbuf = talloc_zero_size(ctx, num_bits);
289 if (!demux->out_bitbuf)
290 return -ENOMEM;
291 demux->out_bitbuf_size = num_bits;
292
293 return 0;
294}
295
296
297static int find_unused_subchan_idx(const struct osmo_i460_timeslot *ts)
298{
299 int i;
300
301 for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
302 const struct osmo_i460_subchan *schan = &ts->schan[i];
303 if (schan->rate == OSMO_I460_RATE_NONE)
304 return i;
305 }
306 return -1;
307}
308
Philipp Maier6509d202020-07-24 21:52:56 +0200309/* reset subchannel struct into a defined state */
310static void subchan_reset(struct osmo_i460_subchan *schan, bool first_time)
311{
312 /* Before we zero out the subchannel struct, we must be sure that the
313 * tx_queue is cleared and all dynamically allocated memory is freed.
314 * However, on an uninitalized subchannel struct we can not be sure
315 * that the pointers are valid. If the subchannel is reset the first
316 * time the caller must set first_time to true. */
317 if (!first_time) {
318 if (schan->demux.out_bitbuf)
319 talloc_free(schan->demux.out_bitbuf);
320 msgb_queue_free(&schan->mux.tx_queue);
321 }
322
323 /* Reset subchannel to a defined state */
324 memset(schan, 0, sizeof(*schan));
325 schan->rate = OSMO_I460_RATE_NONE;
326 INIT_LLIST_HEAD(&schan->mux.tx_queue);
327}
328
Harald Welteb795f032020-05-14 11:42:53 +0200329/*! initialize an I.460 timeslot */
330void osmo_i460_ts_init(struct osmo_i460_timeslot *ts)
331{
332 int i;
333
334 for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
335 struct osmo_i460_subchan *schan = &ts->schan[i];
Philipp Maier6509d202020-07-24 21:52:56 +0200336 subchan_reset(schan, true);
Harald Welteb795f032020-05-14 11:42:53 +0200337 }
338}
339
340/*! add a new sub-channel to the given timeslot
341 * \param[in] ctx talloc context from where to allocate the internal buffer
342 * \param[in] ts timeslot to which to add a sub-channel
343 * \param[in] chd description of the sub-channel to be added
344 * \return pointer to sub-channel on success, NULL on error */
345struct osmo_i460_subchan *
346osmo_i460_subchan_add(void *ctx, struct osmo_i460_timeslot *ts, const struct osmo_i460_schan_desc *chd)
347{
348 struct osmo_i460_subchan *schan;
349 int idx, rc;
350
351 idx = find_unused_subchan_idx(ts);
352 if (idx < 0)
353 return NULL;
354
355 schan = &ts->schan[idx];
356
357 schan->rate = chd->rate;
358 schan->bit_offset = chd->bit_offset;
359
360 schan->demux.out_cb_bits = chd->demux.out_cb_bits;
361 schan->demux.out_cb_bytes = chd->demux.out_cb_bytes;
362 schan->demux.user_data = chd->demux.user_data;
363 rc = alloc_bitbuf(ctx, schan, chd->demux.num_bits);
364 if (rc < 0) {
Philipp Maier6509d202020-07-24 21:52:56 +0200365 subchan_reset(schan, false);
Harald Welteb795f032020-05-14 11:42:53 +0200366 return NULL;
367 }
368
369 /* return number of schan in use */
370 return schan;
371}
372
373/* remove a su-channel from the multiplex */
374void osmo_i460_subchan_del(struct osmo_i460_subchan *schan)
375{
Philipp Maier6509d202020-07-24 21:52:56 +0200376 subchan_reset(schan, false);
Harald Welteb795f032020-05-14 11:42:53 +0200377}
378
379/*! @} */