blob: fc9462d86d72971eeddaf5d6059e306a4ec6d0ad [file] [log] [blame]
Harald Welte04fe6b52020-06-20 21:05:31 +02001#include <sys/types.h>
2#include <sys/stat.h>
3#include <sys/ioctl.h>
4#include <fcntl.h>
5#include <unistd.h>
6#include <errno.h>
7
8#include <dahdi/user.h>
9
10#include <osmocom/core/utils.h>
11#include <osmocom/core/application.h>
12#include <osmocom/gsm/i460_mux.h>
13#include <osmocom/trau/trau_sync.h>
14#include <osmocom/trau/trau_frame.h>
15#include <osmocom/trau/trau_rtp.h>
16#include <osmocom/trau/osmo_ortp.h>
17
18#define D_BCHAN_TX_GRAN 160
19
20/***********************************************************************
21 * BEGIN CONFIGURATION
22 ***********************************************************************/
23/* HACK: Those do not have getopt but need to be modified in source code to match your enviroment */
24
25/* do we just locally loop the calls from 1->2, or do we interface with RTP? */
26static bool g_local_loop = false;
27
28/* remote IP address to which we send RTP data (unless g_local_loop mode) */
29const char *g_remote_host = "192.168.101.131";
30
31/* remote UDP base port to which we send RTP data: +2 for every sub-slot */
32const int g_remote_port = 9000;
33
34/* local UDP base port on which we receive RTP data: +2 for every sub-slot */
35const int g_local_port = 8000;
36
37/* codec; can be OSMO_TRAU16_FT_FR or OSMO_TRAU16_FT_EFR */
38const enum osmo_trau_frame_type g_ftype = OSMO_TRAU16_FT_EFR;
39
40/***********************************************************************
41 * END CONFIGURATION
42 ***********************************************************************/
43
44/* do we have a DAHDI chardev to which we can write RTP->TRAU conversion data/ */
45static bool g_ts_is_writable = false;
46
47struct sc_state {
48 unsigned int num;
49 struct osmo_i460_subchan *i460_sc;
50 struct osmo_fsm_inst *sync_fi;
51 struct osmo_rtp_socket *rtps;
52 struct osmo_trau2rtp_state t2r;
53};
54
55struct state {
56 int in_fd;
57 struct osmo_i460_timeslot i460_ts;
58 struct sc_state sc[4];
59};
60static struct state g_st;
61
62#define LOGSC(sc, fmt, args...) printf("SC%u: " fmt, (sc)->num, ## args)
63
64static struct sc_state *opposite_schan(struct sc_state *sc)
65{
66 /* we blindly assume that there is a call between sub-channel 1 + sub-channel 2 */
67 switch (sc->num) {
68 case 1:
69 return &g_st.sc[2];
70 case 2:
71 return &g_st.sc[1];
72 default:
73 OSMO_ASSERT(0);
74 }
75}
76
77/* called by I.460 de-multeiplexer; feed output of I.460 demux into TRAU frame sync */
78static void i460_demux_bits_cb(struct osmo_i460_subchan *schan, void *user_data,
79 const ubit_t *bits, unsigned int num_bits)
80{
81 struct sc_state *sc = user_data;
82 //printf("I460: %s\n", osmo_ubit_dump(bits, num_bits));
83 osmo_trau_sync_rx_ubits(sc->sync_fi, bits, num_bits);
84}
85
86/* called for each synchronized TRAU frame received; decode frame + convert to RTP */
87static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int num_bits)
88{
89 struct sc_state *sc = user_data;
90 struct osmo_trau_frame fr;
91 int rc;
92
93 LOGSC(sc, "Rx TRAU: %s\n", osmo_ubit_dump(bits, num_bits));
94 if (!bits)
95 goto skip;
96
97 rc = osmo_trau_frame_decode_16k(&fr, bits, OSMO_TRAU_DIR_UL);
98 if (rc != 0)
99 goto skip;
100
101 uint8_t sid;
102 switch (fr.type) {
103 case OSMO_TRAU16_FT_FR:
104 case OSMO_TRAU16_FT_EFR:
105 sid = (fr.c_bits[13-1]) << 1 | (fr.c_bits[14-1] << 0);
106 LOGSC(sc, "-> FT=%s, BFI=%u, SID=%u, TAF=%u DTXd=%u\n",
107 osmo_trau_frame_type_name(fr.type), fr.c_bits[12-1], sid, fr.c_bits[15-1], fr.c_bits[17-1]);
108 break;
109 default:
110 LOGSC(sc, "-> FT=%s\n", osmo_trau_frame_type_name(fr.type));
111 break;
112 }
113
114 if (g_local_loop) {
115 /* Mirror back to other sub-slot */
116 struct sc_state *peer = opposite_schan(sc);
117 if (peer) {
118 struct msgb *msg = msgb_alloc(2*40*8, "mirror");
119 fr.c_bits[12-1] = 1; /* C12 = good u-link frame */
120 memset(&fr.c_bits[13-1], 1, 3); /* C13..C15: spare */
121 fr.c_bits[16-1] = 1; /* C16 = SP[eech]; no DTX */
122 memset(&fr.c_bits[6-1], 0, 6); /* C6..C11: tie alignment */
123 fr.dir = OSMO_TRAU_DIR_DL;
124 rc = osmo_trau_frame_encode(msgb_data(msg), 2*40*8, &fr);
125 OSMO_ASSERT(rc >= 0);
126 msgb_put(msg, rc);
127 osmo_i460_mux_enqueue(peer->i460_sc, msg);
128 }
129 } else {
130 /* Convert to RTP */
131 if (fr.type != OSMO_TRAU16_FT_FR && fr.type != OSMO_TRAU16_FT_EFR)
132 goto skip;
133
134 uint8_t rtpbuf[35];
135 struct osmo_trau2rtp_state t2rs = {
136 .type = fr.type,
137 };
138 memset(rtpbuf, 0, sizeof(rtpbuf));
139 rc = osmo_trau2rtp(rtpbuf, sizeof(rtpbuf), &fr, &t2rs);
140 LOGSC(sc, "Tx RTP: %s\n", osmo_hexdump(rtpbuf, rc));
141 if (rc)
142 osmo_rtp_send_frame_ext(sc->rtps, rtpbuf, rc, 160, false);
143 else {
144 osmo_rtp_skipped_frame(sc->rtps, 160);
145 }
146 return;
147 }
148skip:
149 if (!g_local_loop)
150 osmo_rtp_skipped_frame(sc->rtps, 160);
151}
152
153static int dahdi_set_bufinfo(int fd, int as_sigchan)
154{
155 struct dahdi_bufferinfo bi;
156 int x = 0;
157
158 if (ioctl(fd, DAHDI_GET_BUFINFO, &bi)) {
159 LOGP(DLINP, LOGL_ERROR, "Error getting bufinfo\n");
160 return -EIO;
161 }
162
163 if (as_sigchan) {
164 bi.numbufs = 4;
165 bi.bufsize = 512;
166 } else {
167 bi.numbufs = 8;
168 bi.bufsize = D_BCHAN_TX_GRAN;
169 bi.txbufpolicy = DAHDI_POLICY_WHEN_FULL;
170 }
171
172 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
173 fprintf(stderr, "Error setting DAHDI bufinfo\n");
174 return -EIO;
175 }
176
177 if (!as_sigchan) {
178 if (ioctl(fd, DAHDI_AUDIOMODE, &x)) {
179 fprintf(stderr, "Error setting DAHDI bufinfo\n");
180 return -EIO;
181 }
182 } else {
183 int one = 1;
184 ioctl(fd, DAHDI_HDLCFCSMODE, &one);
185 /* we cannot reliably check for the ioctl return value here
186 * as this command will fail if the slot _already_ was a
187 * signalling slot before :( */
188 }
189 return 0;
190}
191
192static void mux_q_empty_cb(struct osmo_i460_subchan *schan, void *user_data);
193
194/* RTP data was received on the socket */
195static void ortp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *payload,
196 unsigned int payload_len, uint16_t seq_number, uint32_t timestamp, bool marker)
197{
198 struct sc_state *sc = rs->priv;
199 struct osmo_trau_frame fr;
200 int rc;
201
202 LOGSC(sc, "RTP Rx: %s\n", osmo_hexdump_nospc(payload, payload_len));
203
204 sc->t2r.type = g_ftype;
205
206 memset(&fr, 0, sizeof(fr));
207 fr.dir = OSMO_TRAU_DIR_DL;
208 rc = osmo_rtp2trau(&fr, payload, payload_len, &sc->t2r);
209 if (rc < 0) {
210 LOGSC(sc, "Failed to convert RTP to TRAU");
211 return;
212 }
213
214 struct msgb *msg = msgb_alloc(2*40*8, "rtp2trau");
215 rc = osmo_trau_frame_encode(msgb_data(msg), 2*40*8, &fr);
216 OSMO_ASSERT(rc >= 0);
217 msgb_put(msg, rc);
218 osmo_i460_mux_enqueue(sc->i460_sc, msg);
219}
220
221static void init(const char *fname)
222{
223 struct stat st;
224 int rc;
225
226 struct osmo_i460_schan_desc scd16_0 = {
227 .rate = OSMO_I460_RATE_16k,
228 .demux = {
229 .num_bits = 40*8,
230 .out_cb_bytes = NULL,
231 },
232 };
233
234 rc = stat(fname, &st);
235 OSMO_ASSERT(rc == 0);
236 if (S_ISCHR(st.st_mode)) {
237 /* if this is a char device, we assume DAHDI */
238 g_ts_is_writable = true;
239 g_st.in_fd = open(fname, O_RDWR);
240 OSMO_ASSERT(g_st.in_fd >= 0);
241 dahdi_set_bufinfo(g_st.in_fd, false);
242 } else {
243 g_st.in_fd = open(fname, O_RDONLY);
244 OSMO_ASSERT(g_st.in_fd >= 0);
245 }
246
247 osmo_i460_ts_init(&g_st.i460_ts);
248
249 //for (i = 0; i < ARRAY_SIZE(g_st.sc); i++) {
250 for (int i = 1; i < 3; i++) {
251 struct sc_state *sc = &g_st.sc[i];
252 sc->num = i;
253
254 scd16_0.bit_offset = i * 2;
255 scd16_0.demux.user_data = sc;
256 scd16_0.demux.out_cb_bits = i460_demux_bits_cb,
257 scd16_0.mux.in_cb_queue_empty = mux_q_empty_cb;
258 scd16_0.mux.user_data = sc;
259 sc->i460_sc = osmo_i460_subchan_add(NULL, &g_st.i460_ts, &scd16_0);
260 OSMO_ASSERT(sc->i460_sc != NULL);
261
262 char strbuf[16];
263 snprintf(strbuf, sizeof(strbuf), "SC%u", sc->num);
264 sc->sync_fi = osmo_trau_sync_alloc(NULL, strbuf, sync_frame_out_cb, OSMO_TRAU_SYNCP_16_FR_EFR, sc);
265 OSMO_ASSERT(sc->sync_fi);
266 sc->rtps = osmo_rtp_socket_create(NULL, OSMO_RTP_F_POLL);
267 OSMO_ASSERT(sc->rtps);
268 osmo_rtp_socket_set_pt(sc->rtps, RTP_PT_GSM_FULL);
269 sc->rtps->rx_cb = ortp_rx_cb;
270 sc->rtps->priv = sc;
271 osmo_rtp_socket_bind(sc->rtps, "0.0.0.0", g_local_port + i*2);
272 osmo_rtp_socket_connect(sc->rtps, g_remote_host, g_remote_port + i*2);
273 //osmo_rtp_socket_autoconnect(sc->rtps);
274 }
275}
276
277static void mux_q_empty_cb(struct osmo_i460_subchan *schan, void *user_data)
278{
279 struct sc_state *sc = user_data;
280 struct msgb *msg = msgb_alloc(2*40*8, "mux-enq");
281 struct osmo_trau_frame traufr = {
282 .type = g_ftype,
283 .dir = OSMO_TRAU_DIR_DL,
284 };
285 int rc;
286
287 LOGSC(sc, "EMPTY -> Generating Tx\n");
288
289 if (traufr.type == OSMO_TRAU16_FT_EFR) {
290 traufr.c_bits[12-1] = 1; /* C12 = good u-link frame */
291 traufr.c_bits[16-1] = 1; /* C16 = SP[eech]; no DTX */
292 }
293
294 rc = osmo_trau_frame_encode(msgb_data(msg), 2*40*8, &traufr);
295 OSMO_ASSERT(rc >= 0);
296 msgb_put(msg, rc);
297 //LOGSC(sc, "Tx TRAU: %s\n", osmo_ubit_dump(cur, 40*8));
298 osmo_i460_mux_enqueue(sc->i460_sc, msg);
299}
300
301static void process(void)
302{
303 uint8_t buf[D_BCHAN_TX_GRAN];
304 int rc, nread;
305
306 while (rc = read(g_st.in_fd, buf, sizeof(buf))) {
307 OSMO_ASSERT(rc == sizeof(buf));
308 nread = rc;
309 osmo_i460_demux_in(&g_st.i460_ts, buf, nread);
310
311 for (int i = 1; i < 3; i++) {
312 struct sc_state *sc = &g_st.sc[i];
313 int rc2 = osmo_rtp_socket_poll(sc->rtps);
314 sc->rtps->rx_user_ts += 160;
315 //printf("rtp_recv=%d (flags=%x)\n",rc2, sc->rtps->flags);
316 }
317
318 /* write as many bytes as we just received */
319 osmo_i460_mux_out(&g_st.i460_ts, buf, nread);
320 if (g_ts_is_writable) {
321 rc = write(g_st.in_fd, buf, nread);
322 if (rc != nread)
323 printf("rc=%d, nread=%d (%s)\n", rc, nread, strerror(errno));
324 OSMO_ASSERT(rc == nread);
325 }
326 //usleep(20000);
327 }
328}
329
330int main(int argc, char **argv)
331{
332 osmo_init_logging2(NULL, NULL);
333 osmo_fsm_log_addr(false);
334 log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
335 log_set_category_filter(osmo_stderr_target, DLMIB, true, LOGL_DEBUG);
336 osmo_rtp_init(NULL);
337
338 init(argv[1]);
339 process();
340}