blob: 320e781ba581c7f9414c84b22ace462c49da170e [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];
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)
Harald Welteb3b474d2020-08-02 11:54:56 +0200140 demux->out_cb_bytes(schan, demux->user_data, data, data_len);
Harald Welteb795f032020-05-14 11:42:53 +0200141 else {
142 ubit_t bits[data_len*8];
143 osmo_pbit2ubit(bits, data, data_len*8);
Harald Welteb3b474d2020-08-02 11:54:56 +0200144 demux->out_cb_bits(schan, demux->user_data, bits, data_len*8);
Harald Welteb795f032020-05-14 11:42:53 +0200145 }
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 */
Philipp Maierb5518a82020-07-31 19:04:00 +0200178 if (llist_empty(&mux->tx_queue)) {
179 /* User code now has a last chance to put something into the queue. */
180 if (mux->in_cb_queue_empty)
Harald Welteb3b474d2020-08-02 11:54:56 +0200181 mux->in_cb_queue_empty(schan, mux->user_data);
Harald Welteb795f032020-05-14 11:42:53 +0200182
Philipp Maierb5518a82020-07-31 19:04:00 +0200183 /* If the queue is still empty, return idle bits */
184 if (llist_empty(&mux->tx_queue))
185 return 0x01;
186 }
Harald Welteb795f032020-05-14 11:42:53 +0200187 msg = llist_entry(mux->tx_queue.next, struct msgb, list);
188 bit = msgb_pull_u8(msg);
189
190 /* free msgb if we have pulled the last bit */
191 if (msgb_length(msg) <= 0) {
192 llist_del(&msg->list);
193 talloc_free(msg);
194 }
195
196 return bit;
197}
198
199/*! provide one byte with the subchan-specific bits of given sub-channel.
200 * \param[in] schan sub-channel that is to provide bits
201 * \parma[out] mask bitmask of those bits filled in
202 * \returns bits of given sub-channel */
203static uint8_t mux_subchan_provide_bits(struct osmo_i460_subchan *schan, uint8_t *mask)
204{
205 uint8_t outbits = 0;
206 uint8_t outmask;
207
208 switch (schan->rate) {
209 case OSMO_I460_RATE_8k:
210 outbits = mux_schan_provide_bit(schan);
211 outmask = 0x01;
212 break;
213 case OSMO_I460_RATE_16k:
214 outbits |= mux_schan_provide_bit(schan) << 1;
215 outbits |= mux_schan_provide_bit(schan) << 0;
216 outmask = 0x03;
217 break;
218 case OSMO_I460_RATE_32k:
219 outbits |= mux_schan_provide_bit(schan) << 3;
220 outbits |= mux_schan_provide_bit(schan) << 2;
221 outbits |= mux_schan_provide_bit(schan) << 1;
222 outbits |= mux_schan_provide_bit(schan) << 0;
223 outmask = 0x0F;
224 break;
225 case OSMO_I460_RATE_64k:
226 outbits |= mux_schan_provide_bit(schan) << 7;
227 outbits |= mux_schan_provide_bit(schan) << 6;
228 outbits |= mux_schan_provide_bit(schan) << 5;
229 outbits |= mux_schan_provide_bit(schan) << 4;
230 outbits |= mux_schan_provide_bit(schan) << 3;
231 outbits |= mux_schan_provide_bit(schan) << 2;
232 outbits |= mux_schan_provide_bit(schan) << 1;
233 outbits |= mux_schan_provide_bit(schan) << 0;
234 outmask = 0xFF;
235 break;
236 default:
237 OSMO_ASSERT(0);
238 }
239 *mask = outmask << schan->bit_offset;
240 return outbits << schan->bit_offset;
241}
242
243/* provide one byte of multiplexed I.460 bits */
244static uint8_t mux_timeslot_provide_bits(struct osmo_i460_timeslot *ts)
245{
246 int i, count = 0;
247 uint8_t ret = 0xff; /* unused bits must be '1' as per I.460 */
248
249 for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
250 struct osmo_i460_subchan *schan = &ts->schan[i];
251 uint8_t bits, mask;
252
253 if (schan->rate == OSMO_I460_RATE_NONE)
254 continue;
255 count++;
256 bits = mux_subchan_provide_bits(schan, &mask);
257 ret &= ~mask;
258 ret |= bits;
259 }
260
261 return ret;
262}
263
264
265/*! Data from E1 timeslot into de-multiplexer
266 * \param[in] ts timeslot state
267 * \param[out] out caller-provided buffer where to store generated output bytes
268 * \param[in] out_len number of bytes to be stored at out
269 */
270int osmo_i460_mux_out(struct osmo_i460_timeslot *ts, uint8_t *out, size_t out_len)
271{
272 int i;
273
274 /* fast path if entire 64k slot is used */
275 //if (osmo_i460_has_single_64k_schan(ts)) { }
276
277 for (i = 0; i < out_len; i++)
278 out[i] = mux_timeslot_provide_bits(ts);
279
280 return out_len;
281}
282
283
284/***********************************************************************
285 * Initialization / Control
286 ***********************************************************************/
287
288
289static int alloc_bitbuf(void *ctx, struct osmo_i460_subchan *schan, size_t num_bits)
290{
291 struct osmo_i460_subchan_demux *demux = &schan->demux;
292
293 talloc_free(demux->out_bitbuf);
294 demux->out_bitbuf = talloc_zero_size(ctx, num_bits);
295 if (!demux->out_bitbuf)
296 return -ENOMEM;
297 demux->out_bitbuf_size = num_bits;
298
299 return 0;
300}
301
302
303static int find_unused_subchan_idx(const struct osmo_i460_timeslot *ts)
304{
305 int i;
306
307 for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
308 const struct osmo_i460_subchan *schan = &ts->schan[i];
309 if (schan->rate == OSMO_I460_RATE_NONE)
310 return i;
311 }
312 return -1;
313}
314
Philipp Maier6509d202020-07-24 21:52:56 +0200315/* reset subchannel struct into a defined state */
316static void subchan_reset(struct osmo_i460_subchan *schan, bool first_time)
317{
318 /* Before we zero out the subchannel struct, we must be sure that the
319 * tx_queue is cleared and all dynamically allocated memory is freed.
320 * However, on an uninitalized subchannel struct we can not be sure
321 * that the pointers are valid. If the subchannel is reset the first
322 * time the caller must set first_time to true. */
323 if (!first_time) {
324 if (schan->demux.out_bitbuf)
325 talloc_free(schan->demux.out_bitbuf);
326 msgb_queue_free(&schan->mux.tx_queue);
327 }
328
329 /* Reset subchannel to a defined state */
330 memset(schan, 0, sizeof(*schan));
331 schan->rate = OSMO_I460_RATE_NONE;
332 INIT_LLIST_HEAD(&schan->mux.tx_queue);
333}
334
Harald Welteb795f032020-05-14 11:42:53 +0200335/*! initialize an I.460 timeslot */
336void osmo_i460_ts_init(struct osmo_i460_timeslot *ts)
337{
338 int i;
339
340 for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
341 struct osmo_i460_subchan *schan = &ts->schan[i];
Harald Welteeb8240d2020-08-02 11:54:15 +0200342 schan->ts = ts;
Philipp Maier6509d202020-07-24 21:52:56 +0200343 subchan_reset(schan, true);
Harald Welteb795f032020-05-14 11:42:53 +0200344 }
345}
346
347/*! add a new sub-channel to the given timeslot
348 * \param[in] ctx talloc context from where to allocate the internal buffer
349 * \param[in] ts timeslot to which to add a sub-channel
350 * \param[in] chd description of the sub-channel to be added
351 * \return pointer to sub-channel on success, NULL on error */
352struct osmo_i460_subchan *
353osmo_i460_subchan_add(void *ctx, struct osmo_i460_timeslot *ts, const struct osmo_i460_schan_desc *chd)
354{
355 struct osmo_i460_subchan *schan;
356 int idx, rc;
357
358 idx = find_unused_subchan_idx(ts);
359 if (idx < 0)
360 return NULL;
361
362 schan = &ts->schan[idx];
363
364 schan->rate = chd->rate;
365 schan->bit_offset = chd->bit_offset;
366
367 schan->demux.out_cb_bits = chd->demux.out_cb_bits;
368 schan->demux.out_cb_bytes = chd->demux.out_cb_bytes;
369 schan->demux.user_data = chd->demux.user_data;
Philipp Maierb5518a82020-07-31 19:04:00 +0200370 schan->mux.in_cb_queue_empty = chd->mux.in_cb_queue_empty;
371 schan->mux.user_data = chd->mux.user_data;
Harald Welteb795f032020-05-14 11:42:53 +0200372 rc = alloc_bitbuf(ctx, schan, chd->demux.num_bits);
373 if (rc < 0) {
Philipp Maier6509d202020-07-24 21:52:56 +0200374 subchan_reset(schan, false);
Harald Welteb795f032020-05-14 11:42:53 +0200375 return NULL;
376 }
377
378 /* return number of schan in use */
379 return schan;
380}
381
382/* remove a su-channel from the multiplex */
383void osmo_i460_subchan_del(struct osmo_i460_subchan *schan)
384{
Philipp Maier6509d202020-07-24 21:52:56 +0200385 subchan_reset(schan, false);
Harald Welteb795f032020-05-14 11:42:53 +0200386}
387
388/*! @} */