blob: b070bbdcadcf67d94befe49b032422af715eb6e1 [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.
Harald Welteb795f032020-05-14 11:42:53 +020017 */
18
19#include <errno.h>
20
21#include <osmocom/core/bits.h>
22#include <osmocom/core/utils.h>
23#include <osmocom/core/msgb.h>
Harald Welted55a2092022-11-29 22:33:54 +010024#include <osmocom/isdn/i460_mux.h>
Harald Welteb795f032020-05-14 11:42:53 +020025
Philipp Maier54e17822023-02-14 13:50:04 +010026/*! count the number of sub-channels in this I.460 slot.
27 * \param[in] ts timeslot that holds the I.460 subchannels.
28 * \return number of subchannels. */
29int osmo_i460_subchan_count(struct osmo_i460_timeslot *ts)
Harald Welteb795f032020-05-14 11:42:53 +020030{
31 int i, num_used = 0;
32
33 for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
34 if (ts->schan[i].rate != OSMO_I460_RATE_NONE)
35 num_used++;
36 }
37
38 return num_used;
39}
40
41/* does this channel have no sub-streams (single 64k subchannel)? */
42static bool osmo_i460_has_single_64k_schan(struct osmo_i460_timeslot *ts)
43{
44 if (osmo_i460_subchan_count(ts) != 1)
45 return false;
46
47 if (ts->schan[0].rate != OSMO_I460_RATE_64k)
48 return false;
49
50 return true;
51}
52
53/***********************************************************************
54 * Demultiplexer
55 ***********************************************************************/
56
57/* append a single bit to a sub-channel */
58static void demux_subchan_append_bit(struct osmo_i460_subchan *schan, uint8_t bit)
59{
60 struct osmo_i460_subchan_demux *demux = &schan->demux;
61
62 OSMO_ASSERT(demux->out_bitbuf);
63 OSMO_ASSERT(demux->out_idx < demux->out_bitbuf_size);
64
65 demux->out_bitbuf[demux->out_idx++] = bit ? 1 : 0;
66
67 if (demux->out_idx >= demux->out_bitbuf_size) {
68 if (demux->out_cb_bits)
Harald Welteb3b474d2020-08-02 11:54:56 +020069 demux->out_cb_bits(schan, demux->user_data, demux->out_bitbuf, demux->out_idx);
Harald Welteb795f032020-05-14 11:42:53 +020070 else {
71 /* pack bits into bytes */
72 OSMO_ASSERT((demux->out_idx % 8) == 0);
73 unsigned int num_bytes = demux->out_idx / 8;
74 uint8_t bytes[num_bytes];
75 osmo_ubit2pbit(bytes, demux->out_bitbuf, demux->out_idx);
Harald Welteb3b474d2020-08-02 11:54:56 +020076 demux->out_cb_bytes(schan, demux->user_data, bytes, num_bytes);
Harald Welteb795f032020-05-14 11:42:53 +020077 }
78 demux->out_idx = 0;
79 }
80}
81
82/* extract those bits relevant to this schan of each byte in 'data' */
83static void demux_subchan_extract_bits(struct osmo_i460_subchan *schan, const uint8_t *data, size_t data_len)
84{
85 int i;
86
87 for (i = 0; i < data_len; i++) {
88 uint8_t inbyte = data[i];
Harald Welte44964982020-08-02 21:54:30 +020089 /* I.460 defines sub-channel 0 is using bit positions 1+2 (the two
90 * most significant bits, hence we extract msb-first */
91 uint8_t inbits = inbyte << schan->bit_offset;
Harald Welteb795f032020-05-14 11:42:53 +020092
93 /* extract the bits relevant to the given schan */
94 switch (schan->rate) {
95 case OSMO_I460_RATE_8k:
Harald Welte44964982020-08-02 21:54:30 +020096 demux_subchan_append_bit(schan, inbits & 0x80);
Harald Welteb795f032020-05-14 11:42:53 +020097 break;
98 case OSMO_I460_RATE_16k:
Harald Welte44964982020-08-02 21:54:30 +020099 demux_subchan_append_bit(schan, inbits & 0x80);
100 demux_subchan_append_bit(schan, inbits & 0x40);
Harald Welteb795f032020-05-14 11:42:53 +0200101 break;
102 case OSMO_I460_RATE_32k:
Harald Welte44964982020-08-02 21:54:30 +0200103 demux_subchan_append_bit(schan, inbits & 0x80);
104 demux_subchan_append_bit(schan, inbits & 0x40);
105 demux_subchan_append_bit(schan, inbits & 0x20);
106 demux_subchan_append_bit(schan, inbits & 0x10);
Harald Welteb795f032020-05-14 11:42:53 +0200107 break;
108 case OSMO_I460_RATE_64k:
Harald Welteb795f032020-05-14 11:42:53 +0200109 demux_subchan_append_bit(schan, inbits & 0x80);
Harald Welte44964982020-08-02 21:54:30 +0200110 demux_subchan_append_bit(schan, inbits & 0x40);
111 demux_subchan_append_bit(schan, inbits & 0x20);
112 demux_subchan_append_bit(schan, inbits & 0x10);
113 demux_subchan_append_bit(schan, inbits & 0x08);
114 demux_subchan_append_bit(schan, inbits & 0x04);
115 demux_subchan_append_bit(schan, inbits & 0x02);
116 demux_subchan_append_bit(schan, inbits & 0x01);
Harald Welteb795f032020-05-14 11:42:53 +0200117 break;
118 default:
119 OSMO_ASSERT(0);
120 }
121 }
122}
123
Philipp Maier89309112023-02-14 11:33:41 +0100124/*! Feed multiplexed data (from an E1 timeslot) into de-multiplexer.
125 * \param[in] ts timeslot state.
126 * \param[in] data input data bytes as received from E1/T1.
127 * \param[in] data_len length of data in bytes. */
Harald Welteb795f032020-05-14 11:42:53 +0200128void osmo_i460_demux_in(struct osmo_i460_timeslot *ts, const uint8_t *data, size_t data_len)
129{
130 struct osmo_i460_subchan *schan;
131 struct osmo_i460_subchan_demux *demux;
132 int i;
133
134 /* fast path if entire 64k slot is used */
135 if (osmo_i460_has_single_64k_schan(ts)) {
136 schan = &ts->schan[0];
137 demux = &schan->demux;
138 if (demux->out_cb_bytes)
Harald Welteb3b474d2020-08-02 11:54:56 +0200139 demux->out_cb_bytes(schan, demux->user_data, data, data_len);
Harald Welteb795f032020-05-14 11:42:53 +0200140 else {
141 ubit_t bits[data_len*8];
142 osmo_pbit2ubit(bits, data, data_len*8);
Harald Welteb3b474d2020-08-02 11:54:56 +0200143 demux->out_cb_bits(schan, demux->user_data, bits, data_len*8);
Harald Welteb795f032020-05-14 11:42:53 +0200144 }
145 return;
146 }
147
148 /* Slow path iterating over all lchans */
149 for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
150 schan = &ts->schan[i];
151 if (schan->rate == OSMO_I460_RATE_NONE)
152 continue;
153 demux_subchan_extract_bits(schan, data, data_len);
154 }
155}
156
157
158/***********************************************************************
159 * Multiplexer
160 ***********************************************************************/
161
162/*! enqueue a to-be-transmitted message buffer containing unpacked bits */
163void osmo_i460_mux_enqueue(struct osmo_i460_subchan *schan, struct msgb *msg)
164{
165 OSMO_ASSERT(msgb_length(msg) > 0);
166 msgb_enqueue(&schan->mux.tx_queue, msg);
167}
168
169/* mux: pull the next bit out of the given sub-channel */
170static ubit_t mux_schan_provide_bit(struct osmo_i460_subchan *schan)
171{
172 struct osmo_i460_subchan_mux *mux = &schan->mux;
173 struct msgb *msg;
174 ubit_t bit;
175
176 /* if we don't have anything to transmit, return '1' bits */
Philipp Maierb5518a82020-07-31 19:04:00 +0200177 if (llist_empty(&mux->tx_queue)) {
178 /* User code now has a last chance to put something into the queue. */
179 if (mux->in_cb_queue_empty)
Harald Welteb3b474d2020-08-02 11:54:56 +0200180 mux->in_cb_queue_empty(schan, mux->user_data);
Harald Welteb795f032020-05-14 11:42:53 +0200181
Philipp Maierb5518a82020-07-31 19:04:00 +0200182 /* If the queue is still empty, return idle bits */
183 if (llist_empty(&mux->tx_queue))
184 return 0x01;
185 }
Harald Welteb795f032020-05-14 11:42:53 +0200186 msg = llist_entry(mux->tx_queue.next, struct msgb, list);
187 bit = msgb_pull_u8(msg);
188
189 /* free msgb if we have pulled the last bit */
190 if (msgb_length(msg) <= 0) {
191 llist_del(&msg->list);
192 talloc_free(msg);
193 }
194
195 return bit;
196}
197
198/*! provide one byte with the subchan-specific bits of given sub-channel.
199 * \param[in] schan sub-channel that is to provide bits
Vadim Yanitskiy64277a02023-02-28 03:30:27 +0700200 * \param[out] mask bitmask of those bits filled in
Harald Welteb795f032020-05-14 11:42:53 +0200201 * \returns bits of given sub-channel */
202static uint8_t mux_subchan_provide_bits(struct osmo_i460_subchan *schan, uint8_t *mask)
203{
204 uint8_t outbits = 0;
205 uint8_t outmask;
206
Harald Welte44964982020-08-02 21:54:30 +0200207 /* I.460 defines sub-channel 0 is using bit positions 1+2 (the two
208 * most significant bits, hence we provide msb-first */
209
Harald Welteb795f032020-05-14 11:42:53 +0200210 switch (schan->rate) {
211 case OSMO_I460_RATE_8k:
Harald Welte44964982020-08-02 21:54:30 +0200212 outbits = mux_schan_provide_bit(schan) << 7;
213 outmask = 0x80;
Harald Welteb795f032020-05-14 11:42:53 +0200214 break;
215 case OSMO_I460_RATE_16k:
Harald Welte44964982020-08-02 21:54:30 +0200216 outbits |= mux_schan_provide_bit(schan) << 7;
217 outbits |= mux_schan_provide_bit(schan) << 6;
218 outmask = 0xC0;
Harald Welteb795f032020-05-14 11:42:53 +0200219 break;
220 case OSMO_I460_RATE_32k:
Harald Welte44964982020-08-02 21:54:30 +0200221 outbits |= mux_schan_provide_bit(schan) << 7;
222 outbits |= mux_schan_provide_bit(schan) << 6;
223 outbits |= mux_schan_provide_bit(schan) << 5;
224 outbits |= mux_schan_provide_bit(schan) << 4;
225 outmask = 0xF0;
Harald Welteb795f032020-05-14 11:42:53 +0200226 break;
227 case OSMO_I460_RATE_64k:
228 outbits |= mux_schan_provide_bit(schan) << 7;
229 outbits |= mux_schan_provide_bit(schan) << 6;
230 outbits |= mux_schan_provide_bit(schan) << 5;
231 outbits |= mux_schan_provide_bit(schan) << 4;
232 outbits |= mux_schan_provide_bit(schan) << 3;
233 outbits |= mux_schan_provide_bit(schan) << 2;
234 outbits |= mux_schan_provide_bit(schan) << 1;
235 outbits |= mux_schan_provide_bit(schan) << 0;
236 outmask = 0xFF;
237 break;
238 default:
239 OSMO_ASSERT(0);
240 }
Harald Welte44964982020-08-02 21:54:30 +0200241 *mask = outmask >> schan->bit_offset;
242 return outbits >> schan->bit_offset;
Harald Welteb795f032020-05-14 11:42:53 +0200243}
244
245/* provide one byte of multiplexed I.460 bits */
246static uint8_t mux_timeslot_provide_bits(struct osmo_i460_timeslot *ts)
247{
Harald Welteb795f032020-05-14 11:42:53 +0200248 uint8_t ret = 0xff; /* unused bits must be '1' as per I.460 */
249
Vadim Yanitskiy39469b92023-07-28 04:58:00 +0700250 for (int i = 0; i < ARRAY_SIZE(ts->schan); i++) {
Harald Welteb795f032020-05-14 11:42:53 +0200251 struct osmo_i460_subchan *schan = &ts->schan[i];
252 uint8_t bits, mask;
253
254 if (schan->rate == OSMO_I460_RATE_NONE)
255 continue;
Harald Welteb795f032020-05-14 11:42:53 +0200256 bits = mux_subchan_provide_bits(schan, &mask);
257 ret &= ~mask;
258 ret |= bits;
259 }
260
261 return ret;
262}
263
264
Philipp Maier89309112023-02-14 11:33:41 +0100265/*! Get multiplexed data from de-multiplexer (for feeding it into an E1 timeslot).
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. */
Harald Welteb795f032020-05-14 11:42:53 +0200269int osmo_i460_mux_out(struct osmo_i460_timeslot *ts, uint8_t *out, size_t out_len)
270{
271 int i;
272
273 /* fast path if entire 64k slot is used */
274 //if (osmo_i460_has_single_64k_schan(ts)) { }
275
276 for (i = 0; i < out_len; i++)
277 out[i] = mux_timeslot_provide_bits(ts);
278
279 return out_len;
280}
281
282
283/***********************************************************************
284 * Initialization / Control
285 ***********************************************************************/
286
287
288static int alloc_bitbuf(void *ctx, struct osmo_i460_subchan *schan, size_t num_bits)
289{
290 struct osmo_i460_subchan_demux *demux = &schan->demux;
291
292 talloc_free(demux->out_bitbuf);
293 demux->out_bitbuf = talloc_zero_size(ctx, num_bits);
294 if (!demux->out_bitbuf)
295 return -ENOMEM;
296 demux->out_bitbuf_size = num_bits;
297
298 return 0;
299}
300
301
302static int find_unused_subchan_idx(const struct osmo_i460_timeslot *ts)
303{
304 int i;
305
306 for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
307 const struct osmo_i460_subchan *schan = &ts->schan[i];
308 if (schan->rate == OSMO_I460_RATE_NONE)
309 return i;
310 }
311 return -1;
312}
313
Philipp Maier6509d202020-07-24 21:52:56 +0200314/* reset subchannel struct into a defined state */
315static void subchan_reset(struct osmo_i460_subchan *schan, bool first_time)
316{
317 /* Before we zero out the subchannel struct, we must be sure that the
318 * tx_queue is cleared and all dynamically allocated memory is freed.
319 * However, on an uninitalized subchannel struct we can not be sure
320 * that the pointers are valid. If the subchannel is reset the first
321 * time the caller must set first_time to true. */
322 if (!first_time) {
323 if (schan->demux.out_bitbuf)
324 talloc_free(schan->demux.out_bitbuf);
325 msgb_queue_free(&schan->mux.tx_queue);
326 }
327
328 /* Reset subchannel to a defined state */
329 memset(schan, 0, sizeof(*schan));
330 schan->rate = OSMO_I460_RATE_NONE;
331 INIT_LLIST_HEAD(&schan->mux.tx_queue);
332}
333
Harald Welteb795f032020-05-14 11:42:53 +0200334/*! initialize an I.460 timeslot */
335void osmo_i460_ts_init(struct osmo_i460_timeslot *ts)
336{
337 int i;
338
339 for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
340 struct osmo_i460_subchan *schan = &ts->schan[i];
Harald Welteeb8240d2020-08-02 11:54:15 +0200341 schan->ts = ts;
Philipp Maier6509d202020-07-24 21:52:56 +0200342 subchan_reset(schan, true);
Harald Welteb795f032020-05-14 11:42:53 +0200343 }
344}
345
346/*! add a new sub-channel to the given timeslot
347 * \param[in] ctx talloc context from where to allocate the internal buffer
348 * \param[in] ts timeslot to which to add a sub-channel
349 * \param[in] chd description of the sub-channel to be added
350 * \return pointer to sub-channel on success, NULL on error */
351struct osmo_i460_subchan *
352osmo_i460_subchan_add(void *ctx, struct osmo_i460_timeslot *ts, const struct osmo_i460_schan_desc *chd)
353{
354 struct osmo_i460_subchan *schan;
355 int idx, rc;
356
357 idx = find_unused_subchan_idx(ts);
358 if (idx < 0)
359 return NULL;
360
361 schan = &ts->schan[idx];
362
363 schan->rate = chd->rate;
364 schan->bit_offset = chd->bit_offset;
365
366 schan->demux.out_cb_bits = chd->demux.out_cb_bits;
367 schan->demux.out_cb_bytes = chd->demux.out_cb_bytes;
368 schan->demux.user_data = chd->demux.user_data;
Philipp Maierb5518a82020-07-31 19:04:00 +0200369 schan->mux.in_cb_queue_empty = chd->mux.in_cb_queue_empty;
370 schan->mux.user_data = chd->mux.user_data;
Harald Welteb795f032020-05-14 11:42:53 +0200371 rc = alloc_bitbuf(ctx, schan, chd->demux.num_bits);
372 if (rc < 0) {
Philipp Maier6509d202020-07-24 21:52:56 +0200373 subchan_reset(schan, false);
Harald Welteb795f032020-05-14 11:42:53 +0200374 return NULL;
375 }
376
377 /* return number of schan in use */
378 return schan;
379}
380
381/* remove a su-channel from the multiplex */
382void osmo_i460_subchan_del(struct osmo_i460_subchan *schan)
383{
Philipp Maier6509d202020-07-24 21:52:56 +0200384 subchan_reset(schan, false);
Harald Welteb795f032020-05-14 11:42:53 +0200385}
386
387/*! @} */