blob: 91ab2a19bf2bc25997437f06c942b2cf4f85c2ff [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)
Harald Welteb3b474d2020-08-02 11:54:56 +020072 demux->out_cb_bits(schan, demux->user_data, demux->out_bitbuf, demux->out_idx);
Harald Welteb795f032020-05-14 11:42:53 +020073 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);
Harald Welteb3b474d2020-08-02 11:54:56 +020079 demux->out_cb_bytes(schan, demux->user_data, bytes, num_bytes);
Harald Welteb795f032020-05-14 11:42:53 +020080 }
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];
Harald Welte44964982020-08-02 21:54:30 +020092 /* I.460 defines sub-channel 0 is using bit positions 1+2 (the two
93 * most significant bits, hence we extract msb-first */
94 uint8_t inbits = inbyte << schan->bit_offset;
Harald Welteb795f032020-05-14 11:42:53 +020095
96 /* extract the bits relevant to the given schan */
97 switch (schan->rate) {
98 case OSMO_I460_RATE_8k:
Harald Welte44964982020-08-02 21:54:30 +020099 demux_subchan_append_bit(schan, inbits & 0x80);
Harald Welteb795f032020-05-14 11:42:53 +0200100 break;
101 case OSMO_I460_RATE_16k:
Harald Welte44964982020-08-02 21:54:30 +0200102 demux_subchan_append_bit(schan, inbits & 0x80);
103 demux_subchan_append_bit(schan, inbits & 0x40);
Harald Welteb795f032020-05-14 11:42:53 +0200104 break;
105 case OSMO_I460_RATE_32k:
Harald Welte44964982020-08-02 21:54:30 +0200106 demux_subchan_append_bit(schan, inbits & 0x80);
107 demux_subchan_append_bit(schan, inbits & 0x40);
108 demux_subchan_append_bit(schan, inbits & 0x20);
109 demux_subchan_append_bit(schan, inbits & 0x10);
Harald Welteb795f032020-05-14 11:42:53 +0200110 break;
111 case OSMO_I460_RATE_64k:
Harald Welteb795f032020-05-14 11:42:53 +0200112 demux_subchan_append_bit(schan, inbits & 0x80);
Harald Welte44964982020-08-02 21:54:30 +0200113 demux_subchan_append_bit(schan, inbits & 0x40);
114 demux_subchan_append_bit(schan, inbits & 0x20);
115 demux_subchan_append_bit(schan, inbits & 0x10);
116 demux_subchan_append_bit(schan, inbits & 0x08);
117 demux_subchan_append_bit(schan, inbits & 0x04);
118 demux_subchan_append_bit(schan, inbits & 0x02);
119 demux_subchan_append_bit(schan, inbits & 0x01);
Harald Welteb795f032020-05-14 11:42:53 +0200120 break;
121 default:
122 OSMO_ASSERT(0);
123 }
124 }
125}
126
127/*! Data from E1 timeslot into de-multiplexer
128 * \param[in] ts timeslot state
129 * \param[in] data input data bytes as received from E1/T1
130 * \param[in] data_len length of data in bytes */
131void osmo_i460_demux_in(struct osmo_i460_timeslot *ts, const uint8_t *data, size_t data_len)
132{
133 struct osmo_i460_subchan *schan;
134 struct osmo_i460_subchan_demux *demux;
135 int i;
136
137 /* fast path if entire 64k slot is used */
138 if (osmo_i460_has_single_64k_schan(ts)) {
139 schan = &ts->schan[0];
140 demux = &schan->demux;
141 if (demux->out_cb_bytes)
Harald Welteb3b474d2020-08-02 11:54:56 +0200142 demux->out_cb_bytes(schan, demux->user_data, data, data_len);
Harald Welteb795f032020-05-14 11:42:53 +0200143 else {
144 ubit_t bits[data_len*8];
145 osmo_pbit2ubit(bits, data, data_len*8);
Harald Welteb3b474d2020-08-02 11:54:56 +0200146 demux->out_cb_bits(schan, demux->user_data, bits, data_len*8);
Harald Welteb795f032020-05-14 11:42:53 +0200147 }
148 return;
149 }
150
151 /* Slow path iterating over all lchans */
152 for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
153 schan = &ts->schan[i];
154 if (schan->rate == OSMO_I460_RATE_NONE)
155 continue;
156 demux_subchan_extract_bits(schan, data, data_len);
157 }
158}
159
160
161/***********************************************************************
162 * Multiplexer
163 ***********************************************************************/
164
165/*! enqueue a to-be-transmitted message buffer containing unpacked bits */
166void osmo_i460_mux_enqueue(struct osmo_i460_subchan *schan, struct msgb *msg)
167{
168 OSMO_ASSERT(msgb_length(msg) > 0);
169 msgb_enqueue(&schan->mux.tx_queue, msg);
170}
171
172/* mux: pull the next bit out of the given sub-channel */
173static ubit_t mux_schan_provide_bit(struct osmo_i460_subchan *schan)
174{
175 struct osmo_i460_subchan_mux *mux = &schan->mux;
176 struct msgb *msg;
177 ubit_t bit;
178
179 /* if we don't have anything to transmit, return '1' bits */
Philipp Maierb5518a82020-07-31 19:04:00 +0200180 if (llist_empty(&mux->tx_queue)) {
181 /* User code now has a last chance to put something into the queue. */
182 if (mux->in_cb_queue_empty)
Harald Welteb3b474d2020-08-02 11:54:56 +0200183 mux->in_cb_queue_empty(schan, mux->user_data);
Harald Welteb795f032020-05-14 11:42:53 +0200184
Philipp Maierb5518a82020-07-31 19:04:00 +0200185 /* If the queue is still empty, return idle bits */
186 if (llist_empty(&mux->tx_queue))
187 return 0x01;
188 }
Harald Welteb795f032020-05-14 11:42:53 +0200189 msg = llist_entry(mux->tx_queue.next, struct msgb, list);
190 bit = msgb_pull_u8(msg);
191
192 /* free msgb if we have pulled the last bit */
193 if (msgb_length(msg) <= 0) {
194 llist_del(&msg->list);
195 talloc_free(msg);
196 }
197
198 return bit;
199}
200
201/*! provide one byte with the subchan-specific bits of given sub-channel.
202 * \param[in] schan sub-channel that is to provide bits
203 * \parma[out] mask bitmask of those bits filled in
204 * \returns bits of given sub-channel */
205static uint8_t mux_subchan_provide_bits(struct osmo_i460_subchan *schan, uint8_t *mask)
206{
207 uint8_t outbits = 0;
208 uint8_t outmask;
209
Harald Welte44964982020-08-02 21:54:30 +0200210 /* I.460 defines sub-channel 0 is using bit positions 1+2 (the two
211 * most significant bits, hence we provide msb-first */
212
Harald Welteb795f032020-05-14 11:42:53 +0200213 switch (schan->rate) {
214 case OSMO_I460_RATE_8k:
Harald Welte44964982020-08-02 21:54:30 +0200215 outbits = mux_schan_provide_bit(schan) << 7;
216 outmask = 0x80;
Harald Welteb795f032020-05-14 11:42:53 +0200217 break;
218 case OSMO_I460_RATE_16k:
Harald Welte44964982020-08-02 21:54:30 +0200219 outbits |= mux_schan_provide_bit(schan) << 7;
220 outbits |= mux_schan_provide_bit(schan) << 6;
221 outmask = 0xC0;
Harald Welteb795f032020-05-14 11:42:53 +0200222 break;
223 case OSMO_I460_RATE_32k:
Harald Welte44964982020-08-02 21:54:30 +0200224 outbits |= mux_schan_provide_bit(schan) << 7;
225 outbits |= mux_schan_provide_bit(schan) << 6;
226 outbits |= mux_schan_provide_bit(schan) << 5;
227 outbits |= mux_schan_provide_bit(schan) << 4;
228 outmask = 0xF0;
Harald Welteb795f032020-05-14 11:42:53 +0200229 break;
230 case OSMO_I460_RATE_64k:
231 outbits |= mux_schan_provide_bit(schan) << 7;
232 outbits |= mux_schan_provide_bit(schan) << 6;
233 outbits |= mux_schan_provide_bit(schan) << 5;
234 outbits |= mux_schan_provide_bit(schan) << 4;
235 outbits |= mux_schan_provide_bit(schan) << 3;
236 outbits |= mux_schan_provide_bit(schan) << 2;
237 outbits |= mux_schan_provide_bit(schan) << 1;
238 outbits |= mux_schan_provide_bit(schan) << 0;
239 outmask = 0xFF;
240 break;
241 default:
242 OSMO_ASSERT(0);
243 }
Harald Welte44964982020-08-02 21:54:30 +0200244 *mask = outmask >> schan->bit_offset;
245 return outbits >> schan->bit_offset;
Harald Welteb795f032020-05-14 11:42:53 +0200246}
247
248/* provide one byte of multiplexed I.460 bits */
249static uint8_t mux_timeslot_provide_bits(struct osmo_i460_timeslot *ts)
250{
251 int i, count = 0;
252 uint8_t ret = 0xff; /* unused bits must be '1' as per I.460 */
253
254 for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
255 struct osmo_i460_subchan *schan = &ts->schan[i];
256 uint8_t bits, mask;
257
258 if (schan->rate == OSMO_I460_RATE_NONE)
259 continue;
260 count++;
261 bits = mux_subchan_provide_bits(schan, &mask);
262 ret &= ~mask;
263 ret |= bits;
264 }
265
266 return ret;
267}
268
269
270/*! Data from E1 timeslot into de-multiplexer
271 * \param[in] ts timeslot state
272 * \param[out] out caller-provided buffer where to store generated output bytes
273 * \param[in] out_len number of bytes to be stored at out
274 */
275int osmo_i460_mux_out(struct osmo_i460_timeslot *ts, uint8_t *out, size_t out_len)
276{
277 int i;
278
279 /* fast path if entire 64k slot is used */
280 //if (osmo_i460_has_single_64k_schan(ts)) { }
281
282 for (i = 0; i < out_len; i++)
283 out[i] = mux_timeslot_provide_bits(ts);
284
285 return out_len;
286}
287
288
289/***********************************************************************
290 * Initialization / Control
291 ***********************************************************************/
292
293
294static int alloc_bitbuf(void *ctx, struct osmo_i460_subchan *schan, size_t num_bits)
295{
296 struct osmo_i460_subchan_demux *demux = &schan->demux;
297
298 talloc_free(demux->out_bitbuf);
299 demux->out_bitbuf = talloc_zero_size(ctx, num_bits);
300 if (!demux->out_bitbuf)
301 return -ENOMEM;
302 demux->out_bitbuf_size = num_bits;
303
304 return 0;
305}
306
307
308static int find_unused_subchan_idx(const struct osmo_i460_timeslot *ts)
309{
310 int i;
311
312 for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
313 const struct osmo_i460_subchan *schan = &ts->schan[i];
314 if (schan->rate == OSMO_I460_RATE_NONE)
315 return i;
316 }
317 return -1;
318}
319
Philipp Maier6509d202020-07-24 21:52:56 +0200320/* reset subchannel struct into a defined state */
321static void subchan_reset(struct osmo_i460_subchan *schan, bool first_time)
322{
323 /* Before we zero out the subchannel struct, we must be sure that the
324 * tx_queue is cleared and all dynamically allocated memory is freed.
325 * However, on an uninitalized subchannel struct we can not be sure
326 * that the pointers are valid. If the subchannel is reset the first
327 * time the caller must set first_time to true. */
328 if (!first_time) {
329 if (schan->demux.out_bitbuf)
330 talloc_free(schan->demux.out_bitbuf);
331 msgb_queue_free(&schan->mux.tx_queue);
332 }
333
334 /* Reset subchannel to a defined state */
335 memset(schan, 0, sizeof(*schan));
336 schan->rate = OSMO_I460_RATE_NONE;
337 INIT_LLIST_HEAD(&schan->mux.tx_queue);
338}
339
Harald Welteb795f032020-05-14 11:42:53 +0200340/*! initialize an I.460 timeslot */
341void osmo_i460_ts_init(struct osmo_i460_timeslot *ts)
342{
343 int i;
344
345 for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
346 struct osmo_i460_subchan *schan = &ts->schan[i];
Harald Welteeb8240d2020-08-02 11:54:15 +0200347 schan->ts = ts;
Philipp Maier6509d202020-07-24 21:52:56 +0200348 subchan_reset(schan, true);
Harald Welteb795f032020-05-14 11:42:53 +0200349 }
350}
351
352/*! add a new sub-channel to the given timeslot
353 * \param[in] ctx talloc context from where to allocate the internal buffer
354 * \param[in] ts timeslot to which to add a sub-channel
355 * \param[in] chd description of the sub-channel to be added
356 * \return pointer to sub-channel on success, NULL on error */
357struct osmo_i460_subchan *
358osmo_i460_subchan_add(void *ctx, struct osmo_i460_timeslot *ts, const struct osmo_i460_schan_desc *chd)
359{
360 struct osmo_i460_subchan *schan;
361 int idx, rc;
362
363 idx = find_unused_subchan_idx(ts);
364 if (idx < 0)
365 return NULL;
366
367 schan = &ts->schan[idx];
368
369 schan->rate = chd->rate;
370 schan->bit_offset = chd->bit_offset;
371
372 schan->demux.out_cb_bits = chd->demux.out_cb_bits;
373 schan->demux.out_cb_bytes = chd->demux.out_cb_bytes;
374 schan->demux.user_data = chd->demux.user_data;
Philipp Maierb5518a82020-07-31 19:04:00 +0200375 schan->mux.in_cb_queue_empty = chd->mux.in_cb_queue_empty;
376 schan->mux.user_data = chd->mux.user_data;
Harald Welteb795f032020-05-14 11:42:53 +0200377 rc = alloc_bitbuf(ctx, schan, chd->demux.num_bits);
378 if (rc < 0) {
Philipp Maier6509d202020-07-24 21:52:56 +0200379 subchan_reset(schan, false);
Harald Welteb795f032020-05-14 11:42:53 +0200380 return NULL;
381 }
382
383 /* return number of schan in use */
384 return schan;
385}
386
387/* remove a su-channel from the multiplex */
388void osmo_i460_subchan_del(struct osmo_i460_subchan *schan)
389{
Philipp Maier6509d202020-07-24 21:52:56 +0200390 subchan_reset(schan, false);
Harald Welteb795f032020-05-14 11:42:53 +0200391}
392
393/*! @} */